• 1 Introduction
  • 2 Effective Visual Representation
    • 2.1 Required Graphical Components
    • 2.2 Purposes of Viz Representation
    • 2.3 Types of Visual Representation
    • 2.4 Model-based Visualization
      • 2.4.1 Model Performance Evaluation
      • 2.4.2 Understanding Model Predictions
  • 3 R Interactive Graphical Functions
    • 3.1 Visualizing Relationship
    • 3.2 Visualizing Distributions
  • 1 Introduction
  • 2 Effective Visual Representation
    • 2.1 Required Graphical Components
    • 2.2 Purposes of Viz Representation
    • 2.3 Types of Visual Representation
    • 2.4 Model-based Visualization
      • 2.4.1 Model Performance Evaluation
      • 2.4.2 Understanding Model Predictions
  • 3 R Interactive Graphical Functions
    • 3.1 Visualizing Relationship
    • 3.2 Visualizing Distributions


1 Introduction

Technical communication, including report writing and visual representation, is a critical aspect of data science and machine learning (ML). By combining clear report writing and compelling visual representation, data professionals can unlock the full potential of their analyses, fostering understanding, trust, and impactful decision-making. Whether explaining model results, persuading stakeholders, or documenting processes, effective communication ensures the work creates tangible value.

These skills bridge the gap between complex technical work and actionable insights, ensuring that data-driven decisions are effectively communicated to diverse audiences.

2 Effective Visual Representation

Visual representation has become a cornerstone of effective communication. From simple bar charts to complex interactive dashboards, visualizations serve as powerful tools for summarizing, analyzing, and conveying information. The success of any visual representation hinges on its graphical components, which are the fundamental elements that bring data to life. These components, when used effectively, ensure clarity, precision, and engagement.

2.1 Required Graphical Components

col0 = c("#4682B4", "#B4464B", "#B4AF46")
col.vec = ifelse(penguins$species == "Adelie", "#4682B4", 
                 ifelse(penguins$species == "Chinstrap", "#B4464B", "#B4AF46"))
par(mfrow=c(1,2), oma=c(0,0,2,0))
layout <- layout(matrix(c(1,1,2,3), 2, 2, byrow = F)) 
layout <- plot(penguins$bill_length_mm, penguins$bill_depth_mm, 
        col= col.vec,
        pch=16,
        xlab="Bill Depth (mm)",
        ylab="Bill Length (mm)",
        main="A: Bill Length by Bill Depth ",
        cex.main = 0.9,
        col.main = "blue")
        legend("bottomright", c("Adelie", "Chinstrap", "Gentoo"), pch=rep(16,3),
                col = col0,
                cex = 0.7, bty = "n")
layout <- boxplot(penguins$bill_depth_mm ~ penguins$species,
        xlab="Species",
        ylab="Bill Depth (mm)",
        col = col0,
        main="B: Bill Depth by species",
        cex.main = 0.9,
        col.main = "blue")
layout <- boxplot(penguins$bill_length_mm ~ penguins$species,
        xlab="Species",
        ylab="Bill Length (mm)",
        col = col0,
        main="C: Bill Length by species",
        cex.main = 0.9,
        col.main = "blue")
### 
layout <- mtext("Distribution and Coorlation Plots", side = 3, line = -0.5, outer = TRUE)
Graphical elements in an effective visual representation on correlation and numerical variables and their distributions.

Graphical elements in an effective visual representation on correlation and numerical variables and their distributions.

We next briefly explain the key graphical components in the above illustrative figure.

Data Points: The Heart of the Visualization

At the core of any visual representation are the data points. These individual markers encapsulate the values or observations within the dataset, forming the basis for patterns, trends, or relationships. For instance, in a scatter plot, each point represents a unique pairing of variables, while in a line chart, connected points reveal the trajectory of change over time. Without data points, a visualization would lack substance, making them the indispensable foundation of any graphical display.

Axes: The Framework of Interpretation

Axes provide the structural framework that anchors data points in a meaningful context. Typically represented as horizontal and vertical lines, axes define the coordinate space and allow viewers to interpret the scale and scope of the data. The X-axis often represents categories or time intervals, while the Y-axis denotes numerical values or metrics. Proper scaling, labeling, and unit representation on axes are critical for ensuring that data is interpreted accurately. For example, in a bar chart depicting revenue growth, a properly scaled Y-axis starting at zero prevents misrepresentation of trends.

Labels: Guiding the Viewer

Labels are essential textual elements that provide clarity and context to visual components. Axis labels describe the variables being measured, while data labels pinpoint specific values, enhancing the precision of the representation. For instance, a pie chart with clearly labeled slices ensures that viewers can quickly grasp the proportions of different categories. Thoughtfully designed labels not only aid interpretation but also enhance the overall usability of the visualization.

Legends: Decoding the Representation

Legends act as the key to understanding the symbols, colors, or patterns used in a visualization. Particularly in multi-variable representations, legends enable viewers to differentiate between categories or datasets. For example, a line chart with multiple colored lines representing different regions relies on a legend to clarify the mapping between colors and regions. A well-designed legend is concise, strategically placed, and aligned with the overall design of the visualization.

Color: Enhancing Communication

Color is one of the most powerful tools in a visual representation, capable of conveying information, drawing attention, and evoking emotions. Strategic use of color enhances the interpretability of data by differentiating categories, highlighting trends, or emphasizing critical points. For example, a heatmap uses color gradients to represent intensity or magnitude, while contrasting colors in a bar chart distinguish between categories. Accessibility considerations, such as using colorblind-friendly palettes, are essential to ensure inclusivity and broad usability.

Titles, Captions, and Annotations: Providing Context

Titles, captions, and annotations are textual components that offer context, summarize insights, or explain specific aspects of the visualization. The title acts as the headline, succinctly stating the purpose or takeaway of the visual. Captions provide supplementary information, such as data sources or methodology, while annotations highlight key trends, outliers, or benchmarks. Together, these components guide viewers through the data story and reinforce the intended message.


In summary, each graphical component contributes uniquely to the clarity and functionality of a visualization. When thoughtfully integrated, these elements work together to create a design that is intuitive, visually appealing, and effective in communicating its message.

An intuitive visualization ensures the audience can interpret information easily and accurately, without confusion or ambiguity. Aesthetics, such as harmonious color schemes and clean layouts, further enhance engagement and comprehension. By balancing clarity, functionality, and design, a well-crafted visualization transforms data into meaningful insights, empowering storytelling.

2.2 Purposes of Viz Representation

Visual representation plays a pivotal role in the field of data science, especially during Exploratory Data Analysis (EDA) and feature engineering. These two phases are critical for understanding datasets, identifying patterns, and preparing data for machine learning models. The use of visualizations not only simplifies complex data but also enables analysts to uncover insights that may be obscured in raw numbers. In this context, understanding the purposes and types of visual representation becomes essential for effective analysis and feature development.

The purposes of visual representation can be broadly categorized into the following key areas.

Understanding Data Distribution

Visualizations help data scientists examine the distribution of individual variables. Charts such as histograms, density plots, and box plots are commonly used to identify the central tendency, spread, skewness, and presence of outliers. For instance, a histogram may reveal that a variable is right-skewed, suggesting the need for transformation to normalize the data.

Identifying Patterns and Relationships

During EDA, visualizations reveal patterns and relationships between variables. Scatter plots, pair plots, and heatmaps allow analysts to explore correlations and dependencies. For example, a scatter plot might show a strong positive correlation between advertising spend and sales, guiding feature selection for predictive models.

Detecting Anomalies and Outliers

Outliers can significantly impact machine learning models, making their detection crucial. Visual tools like box plots and scatter plots enable analysts to pinpoint anomalies effectively. For instance, a box plot of transaction amounts in a dataset may highlight unusually high values indicative of potential fraud.

Validating Data Quality

Visualizations help assess data quality by highlighting missing values, duplicates, or inconsistent patterns. Heatmaps or bar plots can be used to visualize missing data distributions, guiding the choice of imputation strategies.

Guiding Feature Engineering

Feature engineering involves creating or transforming features to improve model performance. Visualizations such as correlation matrices and decision boundary plots can inspire new feature combinations or transformations. For example, a high correlation between two variables might suggest creating an interaction term, while patterns in a scatter plot could indicate the need for feature scaling.

Communicating Insights

Visualizations are an effective way to communicate findings from EDA and feature engineering to stakeholders. Clear, concise graphs help bridge the gap between technical analysis and business decisions. For instance, a bar chart illustrating the importance of specific features can guide discussions on resource allocation or strategic focus.

2.3 Types of Visual Representation

Visual representation is a cornerstone of Exploratory Data Analysis (EDA) and feature engineering, providing data scientists with tools to understand data, identify patterns, and prepare features for machine learning models. Different types of visualizations serve specific purposes, from understanding variable distributions to detecting outliers and relationships. Below are the primary types of visual representation used in these stages of data science.

Univariate Visualizations

Univariate visualizations focus on the distribution and characteristics of a single variable, helping to assess its central tendency, variability, and potential outliers. Depending on the types feature variables, the following charts are commonly used in practice

  • Histograms: Used to display the frequency distribution of numerical variables, revealing skewness, modality, or gaps in the data.
  • Box Plots: Highlight the spread, median, quartiles, and potential outliers, providing a concise summary of the variable’s distribution.
  • Density Plots: Smoothened versions of histograms that show the probability density function, making them useful for comparing distributions.
  • Bar Charts: Ideal for visualizing the frequency of categorical variables, giving an overview of the dataset composition.
# Simulating sample data
data <- data.frame(Category = rep(c("A", "B"), each = 50),
                   Value = c(rnorm(50, mean = 5), rnorm(50, mean = 7)))

boxplot(data$Value ~ data$Category,
        xlab="Category",
        ylab="Value",
        col = c("skyblue", "purple"),
        main="Comparison between two Distributions",
        cex.main = 1.1,
        col.main = "navy")
Comparing the distribution of two numerical variables

Comparing the distribution of two numerical variables

Bivariate Visualizations

Bivariate visualizations explore relationships between two variables, uncovering dependencies or correlations that may guide feature selection or transformation.

  • Scatter Plots: Used to identify correlations, clusters, or trends between two numerical variables.
  • Line Charts: Suitable for temporal data, showing trends or changes over time.
  • Grouped Bar Charts: Useful for comparing categories across multiple groups or subgroups.
  • Heatmaps: Display correlations between variables using color gradients, making them a popular choice for visualizing correlation matrices.
# Load iris dataset
data(iris)
mrg <- list(l = 50, r = 50, b = 50, t = 50, pad = 20)
##
pal <- c("#1b9e77", "#d95f02", "#7570b3")
pal <- setNames(pal, c("virginica", "setosa", "versicolor"))
#
fig <- plot_ly(data = iris, x = ~Sepal.Length, y = ~Petal.Length, 
               color = ~Species, colors = pal,
                marker = list(size = ~Sepal.Length*2)) %>% 
       layout(title = 'Correlation between Petal Length and Sepal Length', 
              plot_bgcolor = "white", 
              xaxis = list(title = 'Sepal Length (cm)'), 
               yaxis = list(title = 'Sepal Width (cm)'), 
              legend = list(x = 0.7, 
                            y = 0.1, 
                           title=list(text='<b> Species of Iris </b>')),
              margin = mrg)
fig
56781234567
Species of Iris setosaversicolorvirginicaCorrelation between Petal Length and Sepal LengthSepal Length (cm)Sepal Width (cm)

Correlation between sepal length and petal length.

Multivariate Visualizations

Multivariate visualizations provide insights into relationships among three or more variables, helping to identify complex interactions.

  • Pair Plots: Offer a grid of scatter plots for all pairs of variables, along with histograms for individual variables, enabling a quick overview of relationships.
  • Interactive 3D Scatter Plots: Visualize relationships in three dimensions, adding depth to the analysis of feature interactions. caution - static 3D scatter should be avoided.
  • Parallel Coordinates: Represent multiple variables for each data point as connected line segments, useful for visualizing trends in high-dimensional data.
  • Bubble Charts: Add a third dimension to scatter plots through bubble size, often used to incorporate a weight or magnitude factor.
names(iris) = c("Sepal.L", "Sepal.W",  "Petal.L", "Petal.W", "Species" )
p <- ggpairs(iris, columns=1:4, 
             aes(color = factor(Species), alpha = 0.5), 
             upper = list(continuous = wrap("cor", size = 3)),
             margin = list(l = 50, r = 50, b = 50, t = 50, pad = 20)) +
      ggtitle("Pairwised Scatter Plot of Iris Data") + 
      theme(
         plot.title = element_text(hjust = 1),    # Center-align the title
         plot.margin = unit(c(1, 2, 2, 1), "cm")  # adjust the margin of the plot
  )
  #
ggplotly(p)
56780.00.51.01.52.02.52462.02.53.03.54.04.50.00.40.81.22.02.53.03.54.04.5Corr: -0.118 setosa: 0.743***versicolor: 0.526*** virginica: 0.457***246Corr: -0.428*** setosa: 0.178 versicolor: 0.561*** virginica: 0.401** Corr: 0.872*** setosa: 0.267. versicolor: 0.754*** virginica: 0.864***0.00.51.01.52.02.5Corr: 0.963*** setosa: 0.332* versicolor: 0.787*** virginica: 0.322* Corr: -0.366*** setosa: 0.233 versicolor: 0.664*** virginica: 0.538***Corr: 0.818*** setosa: 0.278. versicolor: 0.546*** virginica: 0.281*
Pairwised Scatter Plot of Iris DataSepal.LSepal.WPetal.LPetal.WPetal.WPetal.LSepal.WSepal.L

Pairwise scatter plot of numerical variables in the iris data

Visualizations for Categorical Data

Categorical data often requires distinct types of visual representation to highlight distributions and comparisons.

  • Stacked Bar Charts: Display the composition of categories within subgroups, aiding in the analysis of proportions.
  • Mosaic Plots: Provide a detailed view of the relationships between two or more categorical variables through proportional areas.
  • Violin Plots: Combine box plots and density plots to show the distribution of numerical data across categories.
# Example dataset
data <- data.frame(
  Category = c("A", "A", "B", "B", "C", "C"),
  Subcategory = c("X", "Y", "X", "Y", "X", "Y"),
  Value = c(10, 20, 15, 25, 30, 10)
)
#
# Create the stacked bar chart
plot <- plot_ly(
  data,
  x = ~Category,
  y = ~Value,
  color = ~Subcategory,
  type = "bar"
  ) %>%
  layout(
    barmode = "stack", # Stack the bars
    title = "Stacked Bar Chart",
    xaxis = list(title = "Category"),
    yaxis = list(title = "Value")
   )
# Display the plot
plot

An illustrative example of stacked bar chart to compare the distributions of one categorical variable within categories of the other variable.

Specialized Visualizations for EDA

Certain visualizations cater specifically to the needs of EDA, focusing on data quality and structure.

  • Missing Data Heatmaps: Highlight the distribution and frequency of missing values across the data set, guiding imputation strategies.
  • Histogram Facets: Display distributions of a numerical variable across levels of a categorical variable.
  • Outlier Plots: Highlight anomalies using scatter plots or specialized visualizations like box plot whiskers.
# Example dataset
data <- data.frame(
  Value = c(rnorm(100, mean = 5), rnorm(100, mean = 10)),
  Category = rep(c("Group 1", "Group 2"), each = 100)
)

# Create the faceted histogram
gg = ggplot(data, aes(x = Value, fill = Category)) +
  geom_histogram(aes(color = Category), bins = 20, alpha = 0.7, size = 1) +
  facet_wrap(~ Category, ncol = 1) + # Facet by Category
  scale_color_manual(values = c("Group 1" = "blue", "Group 2" = "green")) +
  scale_fill_manual(values = c("Group 1" = "lightblue", "Group 2" = "lightgreen")) +
  labs(
    title = "Histogram Facets with Colored Boundaries",
    x = "Value",
    y = "Frequency"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5),
    strip.text = element_text(size = 12, face = "bold"),
    legend.position = "none" # Remove legend if it's redundant
  )
ggplotly(gg)
05101520252.55.07.510.012.50510152025
Histogram Facets with Colored BoundariesValueFrequencyGroup 1Group 2

Comparing the distributions numerical vaiable across the categories of a categorical variable.

Visualizations for Feature Engineering

Feature engineering relies on visualizations to assess the effectiveness of transformations, combinations, and feature importance.

  • Correlation Matrices: Display pairwise correlations between numerical variables, helping identify redundant or highly correlated features.
  • Feature Importance Plots: Derived from machine learning models, these plots rank features based on their impact on model performance.
  • Decision Boundary Visualizations: Show the regions created by a model’s predictions, useful for evaluating feature combinations.
  • Interaction Plots: Illustrate the combined effect of two or more features on a target variable, often used in regression or classification problems.
# Example dataset
data <- data.frame(
  Factor1 = rep(c("Low", "High"), each = 6),
  Factor2 = rep(c("A", "B", "C"), times = 4),
  Response = c(5, 6, 7, 10, 11, 12, 4, 5, 6, 9, 8, 10)
)

# Create the interaction plot
interact = ggplot(data, aes(x = Factor2, y = Response, group = Factor1, color = Factor1)) +
  geom_line(size = 1) +  # Lines representing interaction
  geom_point(size = 2) + # Points for data
  labs(
    title = "Interaction Plot",
    x = "Factor 2",
    y = "Response",
    color = "Factor 1"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
    axis.text = element_text(size = 12),
    legend.position = "top"
  )
ggplotly(interact)
ABC4681012
Factor 1HighLow Interaction Plot Factor 2Response

Display the interaction effects in an ANOVA model

Interactive Visualizations

Interactive visualizations enhance exploration by allowing users to manipulate the data dynamically.

  • Dashboards: Combine multiple visualizations into a single interface, enabling filtering, zooming, and slicing of data for deeper insights.
  • Geospatial Maps: Visualize geographic data with layers of interactivity, such as zooming into specific regions or displaying detailed tooltips.
  • Interactive Scatter Plots: Allow users to hover over points for detailed information or filter data based on variable ranges.


In summary, the diversity of visual representation in EDA and feature engineering underscores its importance in understanding and preparing data for analysis. From univariate histograms to interactive dashboards, each type serves a unique purpose in exploring patterns, assessing data quality, and optimizing features for machine learning models. By selecting the appropriate visualization for the task at hand, data scientists can uncover insights, enhance communication, and drive better decision-making.

2.4 Model-based Visualization

Visualization is vital in data science and machine learning (ML) because it transforms data, results, and model insights into comprehensible visual forms, aiding exploration, communication, and decision-making.

2.4.1 Model Performance Evaluation

Visualization is critical for evaluating and comparing model performance.

Example: ROC Curve for Classification

library(pROC)
# Simulated classification results
ID = sample(1:150, 150, replace = FALSE)
true_labels <- c(rep(1, 50), rep(0,100))[ID]
predicted_probs <- c(rnorm(50, 3, 7), rnorm(100, 10,4))[ID]
# Plot ROC curve
roc_obj <- roc(true_labels, predicted_probs)
sen = roc_obj$sensitivities
spe = roc_obj$specificities
## 
par(pty = "s")
plot(1-spe, sen, type = "l", lwd = 2, lty = 1, col = "blue", 
     xlab = "1 - specificity",
     ylab = "sensitivity",
     main = "ROC Curve")
abline(0,1, lty = 2, lwd = 2, col = "red")
legend("bottomright", c("model-based Performance", "Random Guess"), 
       col=c("blue", "red"), lty=1:2, lwd = 2:1, bty = "n", cex = 0.8)

An interactive plot provides more granular information about the underlying plot.

library(pROC)
# Simulated classification results
ID = sample(1:150, 150, replace = FALSE)
true_labels <- c(rep(1, 50), rep(0,100))[ID]
predicted_probs <- c(rnorm(50, 3, 7), rnorm(100, 10,4))[ID]
# Plot ROC curve
roc_obj <- roc(true_labels, predicted_probs)
sen = roc_obj$sensitivities
spe = roc_obj$specificities
SenSpe = data.frame(sen = sen, spe = spe)
## 
ggroc = ggplot(data = SenSpe, mapping = aes(x =(1-spe), y = sen)) +
        geom_rect(aes(xmin = 0, 
                      xmax = 1, 
                      ymin = 0, 
                      ymax = 1),
                      fill = "transparent", 
                      color = "darkgray", 
                      size = 0.4   # default is 0.5
                  ) +
        coord_cartesian(xlim = c(0,1), ylim = c(0,1)) +
        #geom_smooth(method = "loess", span = 0.1, col = "darkblue") + 
        #geom_line(col = "darkblue") +
        geom_path(col = "darkblue") +
        #geom_abline(col="darkred", linetype="dashed") +
        geom_segment(aes(x = 0, y = 0, xend = 1, yend = 1), 
                    color = "darkred", linetype = "dash") +

        ggtitle("Interactive ROC Curve") +
        theme(plot.title = element_text(
                                 family = "serif",  # Font family
                                 face = "bold",     # Font face
                                 color = "navy",    # Font color
                                 size = 14,         # Font size
                                 hjust = 0.5,       # Horizontal adjustment
                                 vjust = 1,         # Vertical adjustment
                                 angle = 0          # Font angle
                                 ),
              aspect.ratio = 1)
            
ggplotly(ggroc)
0.000.250.500.751.000.000.250.500.751.00
Interactive ROC Curve (1 - spe)sen

2.4.2 Understanding Model Predictions

Visualization explains how models arrive at their predictions, essential for trust and transparency.

Example: Feature Importance in Random Forest

library(randomForest)
# Fit a random forest model
rf_model <- randomForest(Species ~ ., data = iris, importance = TRUE)
# Plot feature importance
varImpPlot(rf_model, main = "Feature Importance in Random Forest")

rf_model$importance
             setosa  versicolor   virginica MeanDecreaseAccuracy
Sepal.L 0.044408946 0.021189755 0.049340291          0.037682972
Sepal.W 0.009232606 0.004053565 0.008073661          0.007021898
Petal.L 0.356910263 0.310607891 0.289790133          0.315754991
Petal.W 0.314521971 0.296444967 0.261702035          0.288212492
        MeanDecreaseGini
Sepal.L        10.991629
Sepal.W         2.258437
Petal.L        42.087532
Petal.W        43.879176

3 R Interactive Graphical Functions

Interactive visualizations have become an indispensable tool in modern data science. Whether you’re preparing a report, a presentation, or a dashboard, providing interactive figures can make the data more engaging, insightful, and accessible. In R, several powerful packages allow you to create dynamic, interactive plots. In the previous section, I used ggpairs, plot_ly, ggplotly, and leaflet to illustrate various R plots. In this section, we will compare some popular packages for interactive data visualizations in R, including ggiraph, plotly, ggplotly, and highcharter. These libraries are often used in conjunction with the umbrella-package tidyverse for data manipulation.

We will use these libraries to visualize relationship between two numerical variables and the distribution of numerical variable. The examples are based on the popular Palmer Archipelago (Antarctica) penguin dataset which is similar to the well-known iris. It is a great introductory data set for data exploration and visualization.

include_graphics("img/penguinImage.jpg")
Three Palmer Archipelago (Antarctica) penguins

Three Palmer Archipelago (Antarctica) penguins

A copy of the data with missing values deleted is available at https://pengdsci.github.io/STA552/w02/penguin.csv.

penguins = read.csv("https://pengdsci.github.io/STA552/w02/penguin.csv")

The variables are described below:

  • species a factor denoting penguin species (Adélie, Chinstrap and Gentoo)
  • island a factor denoting island in Palmer Archipelago, Antarctica (Biscoe, Dream or Torgersen)
  • bill_length_mm a number denoting bill length (millimeters)
  • bill_depth_mm a number denoting bill depth (millimeters)
  • flipper_length_mm an integer denoting flipper length (millimeters)
  • body_mass_g an integer denoting body mass (grams)
  • sex a factor denoting penguin sex (female, male)
  • year an integer denoting the study year (2007, 2008, or 2009)

3.1 Visualizing Relationship

Two of the 8 feature variables bill_length_mm and bill_depth_mm will be used to demonstrate the interactive plots to visualize the relationship between them with different R graphical functions.

ggiraph

The interactive graphical function ggiraph() is a wrapper of ggplot(). We first make a ggplot and then call ggiraph() to add interactive feature to the ggplot.

fig_full= penguins %>% 
      ggplot(aes(x = bill_length_mm, 
                 y = bill_depth_mm, 
                 color = species, 
                 shape = species)) + 
      geom_point_interactive( 
                aes(tooltip = paste("bill_length_mm:", bill_length_mm, 
                           "<br>", "bill_depth_mm:", bill_depth_mm)), 
                    size = 2) + 
      geom_smooth_interactive(method = "lm", formula = "y ~ x") +
      labs(x = "Bill length (mm)", 
           y = "Bill width (mm)", 
           title = "Bill length vs. bill width", 
           subtitle = "Using the ggiraph package",
           color = "Species", shape = "Species") +
      theme_minimal() + 
      theme(plot.title = element_text(family = "serif",  # Font family
                                  face = "bold",         # Font face
                                  color = "navy",        # Font color
                                  size = 14,             # Font size
                                  hjust = 0.5,           # Horizontal adjustment
                                  vjust = 1,             # Vertical adjustment
                                  angle = 0,             # Font angle
                                  lineheight = 1),       # Line spacing
        plot.subtitle = element_text(                    # Subtitle customization
                                  face = "bold",         # Font face
                                  color = "navy",        # Font color
                                  size = 12,             # Font size
                                  hjust = 0.5,           # Horizontal adjustment
                                  vjust = 1,             # Vertical adjustment
                                  angle = 0,             # Font angle
                                  lineheight = 1),       # Line spacing
        plot.caption = element_text(hjust = 0.25),  # Caption customization
        plot.tag = element_text(face = "italic"),   # Tag customization
        plot.title.position = "plot",               # Title and subtitle position 
                                                    #  ("plot" or "panel")
        plot.caption.position = "panel",            # Caption position ("plot" or "panel")
        plot.tag.position = "top",                  # Tag position
        plot.margin = unit(c(0,1,4,1), "cm"))       # Margins (t, r, b, l)
girafe(ggobj = fig_full)
15.0 17.5 20.0 40 50 60 Bill length (mm) Bill width (mm) Species Adelie Chinstrap Gentoo Using the ggiraph package Bill length vs. bill width

plot_ly()

In R, plot_ly() is a function from the Plotly package used to create interactive plots directly by specifying the data and plot type. It is a core function in the plotly package and serves as the starting point for building various types of visualizations such as scatter plots, line charts, bar charts, and more. The key features are

  • Dynamic Interactivity: it supports zooming, panning, and tooltips (hover effects) for exploring data interactively.

  • Customizable Visuals: It allows customization of colors, markers, axis labels, legends, and more.

  • Wide Range of Plot Types: It handles scatter plots, bar charts, histograms, 3D plots, heatmaps, and more.

  • Layered Plotting: It enables adding multiple traces (data layers) for creating complex visualizations.

  • Integration: It works seamlessly with R Markdown, Shiny, and dashboards.

plotly.fig = penguins %>% 
    plot_ly(x = ~bill_length_mm, 
            y = ~flipper_length_mm, 
            color = ~species, 
            symbol = ~species,
            type = "scatter", 
            mode = "markers",  
            marker = list(size = 10)) %>% 
    layout(
            plot_bgcolor = 'white',
            xaxis = list(title = "Bill Length (mm)", 
                         zeroline = FALSE, 
                         ticklen = 5),
             yaxis = list(title = "Flipper Length (mm)", 
                          zeroline = FALSE, 
                          ticklen = 5),
            title = "Bill length vs. bill width",
            legend = list(x = 0.01, 
                          y = 0.99, 
                          title=list(text='<b> Species of Penguins </b>')),
            margin = list(l = 50, r = 50, b = 50, t = 50, pad = 20)
  ) 
plotly.fig
354045505560170180190200210220230
Species of Penguins AdelieChinstrapGentooBill length vs. bill widthBill Length (mm)Flipper Length (mm)

ggplotly

ggplotly() is a powerful tool for adding interactivity to existing ggplot2 plots in R. It bridges the gap between static and dynamic visualizations, allowing users to retain the familiar ggplot2 workflow while benefiting from Plotly’s interactivity. This makes it an ideal choice for exploratory data analysis, presentations, and dynamic reporting.

The key features of ggplotly() are

  • Interactivity: It adds zoom, pan, hover tooltips, and legend interaction to static ggplot2 plots.

  • Preserves ggplot2 Aesthetics: It maintains the visual design and customization of ggplot2 plots while making them interactive.

  • Compatibility: It works seamlessly with ggplot2-based plots and integrates well with R Markdown, Shiny apps, and dashboards.

  • Customization: It allows further enhancement of interactivity by modifying tooltips, legends, and layouts.

fig_full <- penguins %>% 
  ggplot(aes(x = bill_length_mm, y = bill_depth_mm, 
             color = species, shape = species)) + 
  geom_point(size = 2) + 
  geom_smooth(method = "lm", formula = "y ~ x") +
  labs(x = "Bill length (mm)", 
       y = "Bill width (mm)", 
       title = "Bill length vs. bill width", 
       subtitle = "Using ggplot2 and ggplotly() from plotly",
       color = "Species", 
       shape = "Species") +
  theme_minimal() +
      theme(plot.title = element_text(family = "serif",  # Font family
                                  face = "bold",         # Font face
                                  color = "navy",        # Font color
                                  size = 14,             # Font size
                                  hjust = 0.5,           # Horizontal adjustment
                                  vjust = 1,             # Vertical adjustment
                                  angle = 0,             # Font angle
                                  lineheight = 1),       # Line spacing
        plot.subtitle = element_text(                    # Subtitle customization
                                  face = "bold",         # Font face
                                  color = "navy",        # Font color
                                  size = 12,             # Font size
                                  hjust = 0.5,           # Horizontal adjustment
                                  vjust = 1,             # Vertical adjustment
                                  angle = 0),            # Font angle
        plot.caption = element_text(hjust = 0.25),  # Caption customization
        plot.tag = element_text(face = "italic"),   # Tag customization
        plot.title.position = "plot",               # Title and subtitle position 
                                                    #  ("plot" or "panel")
        plot.caption.position = "panel",            # Caption position ("plot" or "panel")
        plot.tag.position = "top",                  # Tag position
        plot.margin = unit(c(1,1,1,1), "cm"))       # Margins (t, r, b, l)


ggplotly(fig_full)
40506015.017.520.0
SpeciesAdelieChinstrapGentoo Bill length vs. bill width Bill length (mm)Bill width (mm)

highcharter()

In R, highcharter is a package that provides an interface to the Highcharts JavaScript library, allowing users to create highly interactive and customizable visualizations. It is particularly well-suited for creating charts with advanced interactivity, polished aesthetics, and a wide range of chart types, making it popular for dashboards, presentations, and web-based reporting.

The key features of highcharter():

  • Wide Range of Chart Types: It includes bar charts, line charts, scatter plots, heatmaps, treemaps, and more. It also supports advanced visualizations like gauge charts, stock charts, and maps.

  • Interactive Features: It allows hover tooltips, zooming, panning, and legends. It also allows dynamic updates and animations.

  • Customizability: It offers detailed control over chart aesthetics, including colors, themes, axis labels, and tooltips.

  • Integration: It works seamlessly with tidyverse for data manipulation. It is compatible with R Markdown and Shiny for creating dynamic reports and dashboards.

  • Support for Themes: It allows predefined themes such as hc_theme_smpl() and the ability to create custom themes.

#penguins$colors = ifelse(penguins$species =="Adelie","#440154",
#                         ifelse(penguins$species =="Chinstrap", "#21908C", "#FDE725" ))
##
fig_highchart <- penguins %>% 
  hchart(type = "scatter", 
         hcaes(x = bill_length_mm, 
               y = flipper_length_mm,
               group = species), 
         marker = list(radius = 5),
         showInLegend = F)  %>%  
  hc_xAxis(title = list(text = "Bill length (mm)")) %>%  
  hc_yAxis(title = list(text = "Bill width (mm)")) %>%  
  hc_title(text = "Bill length vs. bill width") %>% 
  #hc_colors(colors) %>%
  hc_legend(align = "left", 
            verticalAlign = "top",
            layout = "vertical", 
            x = 0, 
            y = 100)
##
species_unique <- sort(unique(penguins$species))
colors <- c("Adelie" = "#2caffe", "Chinstrap" = "#544fc5", "Gentoo" = "#00e272")
###
fig_highchart0 = fig_highchart
for(j in 1:3) {
  penguins_subset <- penguins %>%  
    filter(species == species_unique[j])
  ##
  regression <- augment(lm(flipper_length_mm ~ bill_length_mm, 
                   data = penguins_subset))
  ##
  fig_highchart01 <- fig_highchart0 %>%  
      hc_add_series(regression, "line", hcaes(x = bill_length_mm, y = .fitted), 
                    color = colors[j])
  ##
  fig_highchart0 = fig_highchart01  # updating model object to add another line
}
##
fig_highchart01
Created with Highcharts 9.3.1Bill length (mm)Bill width (mm)Bill length vs. bill widthSeries 4Series 5Series 63540455055160170180190200210220230240

3.2 Visualizing Distributions

In this subsection, we draw density curves of BMI across the species of penguins with the above four graphical functions.

ggiraph

fig_density <- penguins %>% 
         ggplot(aes(x = body_mass_g, color = species, fill = species)) +
                geom_density_interactive(
                        aes(tooltip = paste("Species:", species)),
                        linewidth = 0.75, 
                        alpha = 0.5 )  +
                        xlab("BMI Index (g)") +  
           ggtitle("Density Curve of BMI across Species") +
           theme(plot.margin = unit(c(0,1,4,1), "cm"),
                plot.title = element_text(hjust = 0.5))
girafe(ggobj = fig_density)
0e+00 3e-04 6e-04 9e-04 3000 4000 5000 6000 BMI Index (g) density species Adelie Chinstrap Gentoo Density Curve of BMI across Species

plot_ly()

Adelie.BMI <- penguins$body_mass_g[penguins$species=="Adelie"]
Chinstrap.BMI  <- penguins$body_mass_g[penguins$species=="Chinstrap"]
Gentoo.BMI  <- penguins$body_mass_g[penguins$species=="Gentoo"]
Adelie.fit= density(Adelie.BMI)
Chinstrap.fit= density(Chinstrap.BMI)
Gentoo.fit= density(Gentoo.BMI)
# Create overlay density curves
plot_ly() %>%
add_trace(x = Adelie.fit$x, 
          y = Adelie.fit$y, 
          mode = "lines", 
          fill = "tozeroy", 
          yaxis = "y2", 
          name = "Adelie",
          line = list(color = "blue"),
          opacity = 0.6) %>% 
  add_trace(x = Chinstrap.fit$x, 
          y = Chinstrap.fit$y, 
          mode = "lines", 
          fill = "tozeroy", 
          yaxis = "y2", 
          name = "Chinstrap",
          line = list(color = "red"),
          opacity = 0.6) %>%
    add_trace(x = Gentoo.fit$x, 
          y = Gentoo.fit$y, 
          mode = "lines", 
          fill = "tozeroy", 
          yaxis = "y2", 
          name = "Gentoo",
          line = list(color = "purple"),
          opacity = 0.6) %>%
  layout(
        title = "Distributions of BMI across Penguin Species ",
        xaxis = list(title = "BMI Index"),
        yaxis = list(title = "Density"),
        legend = list(title = list(text = "Species", orientation='h')),
        margin = list(l = 100, r = 50, b = 70, t = 100, pad = 20)
     )
300040005000600000.00020.00040.00060.00080.001
SpeciesAdelieChinstrapGentooDistributions of BMI across Penguin SpeciesBMI Index

ggplotly()

ggplotly_density <- penguins %>% 
  ggplot(aes(x = body_mass_g, color = species, fill = species)) +
  geom_density(linewidth = 0.75, alpha = 0.5) +
  xlab("BMI Index (g)") + 
  ggtitle("Density Curve of BMI across Species") +
           theme(plot.margin = unit(c(2,1,2,1), "cm"),
                plot.title = element_text(hjust = 0.5))
ggplotly(ggplotly_density)
30004000500060000e+003e-046e-049e-04
speciesAdelieChinstrapGentooDensity Curve of BMI across SpeciesBMI Index (g)density

highcharter()

Adelie <- penguins %>% filter(species == "Adelie")
Chinstrap <- penguins %>% filter(species == "Chinstrap")
Gentoo <- penguins %>% filter(species == "Gentoo")
hc <- hchart(
             density(Adelie$body_mass_g), 
             type = "area", 
             color = "steelblue", 
             name = "Adelie") %>%
      hc_add_series(
             density(Chinstrap$body_mass_g), 
             type = "area",
             color = "#B71C1C", 
             name = "Chinstrap") %>%
      hc_add_series(
             density(Gentoo$body_mass_g), 
             type = "area",
             color = "purple", 
             name = "Gentoo") %>%
      hc_title(text = "Density Curves of BMI across Species") %>% 
      hc_legend(align = "left", 
            verticalAlign = "top",
            layout = "vertical", 
            x = 0, 
            y = 100)
hc
Created with Highcharts 9.3.1Density Curves of BMI across SpeciesAdelieChinstrapGentoo3k4k5k6k00.00020.00040.00060.00080.0010.0012
LS0tDQp0aXRsZTogJ1Zpc3VhbCBSZXByZXNlbnRhdGlvbiBmb3IgTWFjaGluZSBMZWFybmluZycNCmF1dGhvcjogIkNoZW5nIFBlbmciDQpkYXRlOiAiICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICBmaWdfd2lkdGg6IDMNCiAgICBmaWdfaGVpZ2h0OiAzDQplZGl0b3Jfb3B0aW9uczogDQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDIycHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInBhbG1lcnBlbmd1aW5zIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIikNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KbGlicmFyeShHR2FsbHkpDQp9DQppZiAoIXJlcXVpcmUoIm5hbmlhciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuYW5pYXIiKQ0KbGlicmFyeShuYW5pYXIpDQp9DQppZiAoIXJlcXVpcmUoInBvb2wiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicG9vbCIpDQpsaWJyYXJ5KHBvb2wpDQp9DQppZiAoIXJlcXVpcmUoIkRCSSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJEQkkiKQ0KbGlicmFyeShEQkkpDQp9DQppZiAoIXJlcXVpcmUoIlJNeVNRTCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJSTXlTUUwiKQ0KbGlicmFyeShSTXlTUUwpDQp9DQppZiAoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQp9DQppZiAoIXJlcXVpcmUoImdnaXJhcGgiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dpcmFwaCIpDQpsaWJyYXJ5KGdnaXJhcGgpDQp9DQppZiAoIXJlcXVpcmUoImhpZ2hjaGFydGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImhpZ2hjaGFydGVyIikNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpDQp9DQppZiAoIXJlcXVpcmUoImJyb29tIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImJyb29tIikNCmxpYnJhcnkoYnJvb20pDQp9DQojIyANCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0IGZpbGUNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBvdXRwdXQgZmlsZS4gDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4NCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BDQogICAgICAgICAgICAgICAgICAgICAgKSAgDQpgYGANCg0KXA0KDQojIEludHJvZHVjdGlvbg0KDQpUZWNobmljYWwgY29tbXVuaWNhdGlvbiwgaW5jbHVkaW5nIHJlcG9ydCB3cml0aW5nIGFuZCB2aXN1YWwgcmVwcmVzZW50YXRpb24sIGlzIGEgY3JpdGljYWwgYXNwZWN0IG9mIGRhdGEgc2NpZW5jZSBhbmQgbWFjaGluZSBsZWFybmluZyAoTUwpLiBCeSBjb21iaW5pbmcgY2xlYXIgcmVwb3J0IHdyaXRpbmcgYW5kIGNvbXBlbGxpbmcgdmlzdWFsIHJlcHJlc2VudGF0aW9uLCBkYXRhIHByb2Zlc3Npb25hbHMgY2FuIHVubG9jayB0aGUgZnVsbCBwb3RlbnRpYWwgb2YgdGhlaXIgYW5hbHlzZXMsIGZvc3RlcmluZyB1bmRlcnN0YW5kaW5nLCB0cnVzdCwgYW5kIGltcGFjdGZ1bCBkZWNpc2lvbi1tYWtpbmcuIFdoZXRoZXIgZXhwbGFpbmluZyBtb2RlbCByZXN1bHRzLCBwZXJzdWFkaW5nIHN0YWtlaG9sZGVycywgb3IgZG9jdW1lbnRpbmcgcHJvY2Vzc2VzLCBlZmZlY3RpdmUgY29tbXVuaWNhdGlvbiBlbnN1cmVzIHRoZSB3b3JrIGNyZWF0ZXMgdGFuZ2libGUgdmFsdWUuDQoNClRoZXNlIHNraWxscyBicmlkZ2UgdGhlIGdhcCBiZXR3ZWVuIGNvbXBsZXggdGVjaG5pY2FsIHdvcmsgYW5kIGFjdGlvbmFibGUgaW5zaWdodHMsIGVuc3VyaW5nIHRoYXQgZGF0YS1kcml2ZW4gZGVjaXNpb25zIGFyZSBlZmZlY3RpdmVseSBjb21tdW5pY2F0ZWQgdG8gZGl2ZXJzZSBhdWRpZW5jZXMuIA0KDQoNCiMgRWZmZWN0aXZlIFZpc3VhbCBSZXByZXNlbnRhdGlvbg0KDQpWaXN1YWwgcmVwcmVzZW50YXRpb24gaGFzIGJlY29tZSBhIGNvcm5lcnN0b25lIG9mIGVmZmVjdGl2ZSBjb21tdW5pY2F0aW9uLiBGcm9tIHNpbXBsZSBiYXIgY2hhcnRzIHRvIGNvbXBsZXggaW50ZXJhY3RpdmUgZGFzaGJvYXJkcywgdmlzdWFsaXphdGlvbnMgc2VydmUgYXMgcG93ZXJmdWwgdG9vbHMgZm9yIHN1bW1hcml6aW5nLCBhbmFseXppbmcsIGFuZCBjb252ZXlpbmcgaW5mb3JtYXRpb24uIFRoZSBzdWNjZXNzIG9mIGFueSB2aXN1YWwgcmVwcmVzZW50YXRpb24gaGluZ2VzIG9uIGl0cyBncmFwaGljYWwgY29tcG9uZW50cywgd2hpY2ggYXJlIHRoZSBmdW5kYW1lbnRhbCBlbGVtZW50cyB0aGF0IGJyaW5nIGRhdGEgdG8gbGlmZS4gVGhlc2UgY29tcG9uZW50cywgd2hlbiB1c2VkIGVmZmVjdGl2ZWx5LCBlbnN1cmUgY2xhcml0eSwgcHJlY2lzaW9uLCBhbmQgZW5nYWdlbWVudC4NCg0KDQojIyBSZXF1aXJlZCBHcmFwaGljYWwgQ29tcG9uZW50cw0KDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0iR3JhcGhpY2FsIGVsZW1lbnRzIGluIGFuIGVmZmVjdGl2ZSB2aXN1YWwgcmVwcmVzZW50YXRpb24gb24gY29ycmVsYXRpb24gYW5kIG51bWVyaWNhbCB2YXJpYWJsZXMgYW5kIHRoZWlyIGRpc3RyaWJ1dGlvbnMuIn0NCmNvbDAgPSBjKCIjNDY4MkI0IiwgIiNCNDQ2NEIiLCAiI0I0QUY0NiIpDQpjb2wudmVjID0gaWZlbHNlKHBlbmd1aW5zJHNwZWNpZXMgPT0gIkFkZWxpZSIsICIjNDY4MkI0IiwgDQogICAgICAgICAgICAgICAgIGlmZWxzZShwZW5ndWlucyRzcGVjaWVzID09ICJDaGluc3RyYXAiLCAiI0I0NDY0QiIsICIjQjRBRjQ2IikpDQpwYXIobWZyb3c9YygxLDIpLCBvbWE9YygwLDAsMiwwKSkNCmxheW91dCA8LSBsYXlvdXQobWF0cml4KGMoMSwxLDIsMyksIDIsIDIsIGJ5cm93ID0gRikpIA0KbGF5b3V0IDwtIHBsb3QocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0sIHBlbmd1aW5zJGJpbGxfZGVwdGhfbW0sIA0KICAgICAgICBjb2w9IGNvbC52ZWMsDQogICAgICAgIHBjaD0xNiwNCiAgICAgICAgeGxhYj0iQmlsbCBEZXB0aCAobW0pIiwNCiAgICAgICAgeWxhYj0iQmlsbCBMZW5ndGggKG1tKSIsDQogICAgICAgIG1haW49IkE6IEJpbGwgTGVuZ3RoIGJ5IEJpbGwgRGVwdGggIiwNCiAgICAgICAgY2V4Lm1haW4gPSAwLjksDQogICAgICAgIGNvbC5tYWluID0gImJsdWUiKQ0KICAgICAgICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYygiQWRlbGllIiwgIkNoaW5zdHJhcCIsICJHZW50b28iKSwgcGNoPXJlcCgxNiwzKSwNCiAgICAgICAgICAgICAgICBjb2wgPSBjb2wwLA0KICAgICAgICAgICAgICAgIGNleCA9IDAuNywgYnR5ID0gIm4iKQ0KbGF5b3V0IDwtIGJveHBsb3QocGVuZ3VpbnMkYmlsbF9kZXB0aF9tbSB+IHBlbmd1aW5zJHNwZWNpZXMsDQogICAgICAgIHhsYWI9IlNwZWNpZXMiLA0KICAgICAgICB5bGFiPSJCaWxsIERlcHRoIChtbSkiLA0KICAgICAgICBjb2wgPSBjb2wwLA0KICAgICAgICBtYWluPSJCOiBCaWxsIERlcHRoIGJ5IHNwZWNpZXMiLA0KICAgICAgICBjZXgubWFpbiA9IDAuOSwNCiAgICAgICAgY29sLm1haW4gPSAiYmx1ZSIpDQpsYXlvdXQgPC0gYm94cGxvdChwZW5ndWlucyRiaWxsX2xlbmd0aF9tbSB+IHBlbmd1aW5zJHNwZWNpZXMsDQogICAgICAgIHhsYWI9IlNwZWNpZXMiLA0KICAgICAgICB5bGFiPSJCaWxsIExlbmd0aCAobW0pIiwNCiAgICAgICAgY29sID0gY29sMCwNCiAgICAgICAgbWFpbj0iQzogQmlsbCBMZW5ndGggYnkgc3BlY2llcyIsDQogICAgICAgIGNleC5tYWluID0gMC45LA0KICAgICAgICBjb2wubWFpbiA9ICJibHVlIikNCiMjIyANCmxheW91dCA8LSBtdGV4dCgiRGlzdHJpYnV0aW9uIGFuZCBDb29ybGF0aW9uIFBsb3RzIiwgc2lkZSA9IDMsIGxpbmUgPSAtMC41LCBvdXRlciA9IFRSVUUpDQoNCmBgYA0KDQpXZSBuZXh0IGJyaWVmbHkgZXhwbGFpbiB0aGUga2V5IGdyYXBoaWNhbCBjb21wb25lbnRzIGluIHRoZSBhYm92ZSBpbGx1c3RyYXRpdmUgZmlndXJlLg0KDQoNCioqRGF0YSBQb2ludHM6IFRoZSBIZWFydCBvZiB0aGUgVmlzdWFsaXphdGlvbioqDQoNCkF0IHRoZSBjb3JlIG9mIGFueSB2aXN1YWwgcmVwcmVzZW50YXRpb24gYXJlIHRoZSBkYXRhIHBvaW50cy4gVGhlc2UgaW5kaXZpZHVhbCBtYXJrZXJzIGVuY2Fwc3VsYXRlIHRoZSB2YWx1ZXMgb3Igb2JzZXJ2YXRpb25zIHdpdGhpbiB0aGUgZGF0YXNldCwgZm9ybWluZyB0aGUgYmFzaXMgZm9yIHBhdHRlcm5zLCB0cmVuZHMsIG9yIHJlbGF0aW9uc2hpcHMuIEZvciBpbnN0YW5jZSwgaW4gYSBzY2F0dGVyIHBsb3QsIGVhY2ggcG9pbnQgcmVwcmVzZW50cyBhIHVuaXF1ZSBwYWlyaW5nIG9mIHZhcmlhYmxlcywgd2hpbGUgaW4gYSBsaW5lIGNoYXJ0LCBjb25uZWN0ZWQgcG9pbnRzIHJldmVhbCB0aGUgdHJhamVjdG9yeSBvZiBjaGFuZ2Ugb3ZlciB0aW1lLiBXaXRob3V0IGRhdGEgcG9pbnRzLCBhIHZpc3VhbGl6YXRpb24gd291bGQgbGFjayBzdWJzdGFuY2UsIG1ha2luZyB0aGVtIHRoZSBpbmRpc3BlbnNhYmxlIGZvdW5kYXRpb24gb2YgYW55IGdyYXBoaWNhbCBkaXNwbGF5Lg0KDQoqKkF4ZXM6IFRoZSBGcmFtZXdvcmsgb2YgSW50ZXJwcmV0YXRpb24qKg0KDQpBeGVzIHByb3ZpZGUgdGhlIHN0cnVjdHVyYWwgZnJhbWV3b3JrIHRoYXQgYW5jaG9ycyBkYXRhIHBvaW50cyBpbiBhIG1lYW5pbmdmdWwgY29udGV4dC4gVHlwaWNhbGx5IHJlcHJlc2VudGVkIGFzIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGxpbmVzLCBheGVzIGRlZmluZSB0aGUgY29vcmRpbmF0ZSBzcGFjZSBhbmQgYWxsb3cgdmlld2VycyB0byBpbnRlcnByZXQgdGhlIHNjYWxlIGFuZCBzY29wZSBvZiB0aGUgZGF0YS4gVGhlIFgtYXhpcyBvZnRlbiByZXByZXNlbnRzIGNhdGVnb3JpZXMgb3IgdGltZSBpbnRlcnZhbHMsIHdoaWxlIHRoZSBZLWF4aXMgZGVub3RlcyBudW1lcmljYWwgdmFsdWVzIG9yIG1ldHJpY3MuIFByb3BlciBzY2FsaW5nLCBsYWJlbGluZywgYW5kIHVuaXQgcmVwcmVzZW50YXRpb24gb24gYXhlcyBhcmUgY3JpdGljYWwgZm9yIGVuc3VyaW5nIHRoYXQgZGF0YSBpcyBpbnRlcnByZXRlZCBhY2N1cmF0ZWx5LiBGb3IgZXhhbXBsZSwgaW4gYSBiYXIgY2hhcnQgZGVwaWN0aW5nIHJldmVudWUgZ3Jvd3RoLCBhIHByb3Blcmx5IHNjYWxlZCBZLWF4aXMgc3RhcnRpbmcgYXQgemVybyBwcmV2ZW50cyBtaXNyZXByZXNlbnRhdGlvbiBvZiB0cmVuZHMuDQoNCioqTGFiZWxzOiBHdWlkaW5nIHRoZSBWaWV3ZXIqKg0KDQpMYWJlbHMgYXJlIGVzc2VudGlhbCB0ZXh0dWFsIGVsZW1lbnRzIHRoYXQgcHJvdmlkZSBjbGFyaXR5IGFuZCBjb250ZXh0IHRvIHZpc3VhbCBjb21wb25lbnRzLiBBeGlzIGxhYmVscyBkZXNjcmliZSB0aGUgdmFyaWFibGVzIGJlaW5nIG1lYXN1cmVkLCB3aGlsZSBkYXRhIGxhYmVscyBwaW5wb2ludCBzcGVjaWZpYyB2YWx1ZXMsIGVuaGFuY2luZyB0aGUgcHJlY2lzaW9uIG9mIHRoZSByZXByZXNlbnRhdGlvbi4gRm9yIGluc3RhbmNlLCBhIHBpZSBjaGFydCB3aXRoIGNsZWFybHkgbGFiZWxlZCBzbGljZXMgZW5zdXJlcyB0aGF0IHZpZXdlcnMgY2FuIHF1aWNrbHkgZ3Jhc3AgdGhlIHByb3BvcnRpb25zIG9mIGRpZmZlcmVudCBjYXRlZ29yaWVzLiBUaG91Z2h0ZnVsbHkgZGVzaWduZWQgbGFiZWxzIG5vdCBvbmx5IGFpZCBpbnRlcnByZXRhdGlvbiBidXQgYWxzbyBlbmhhbmNlIHRoZSBvdmVyYWxsIHVzYWJpbGl0eSBvZiB0aGUgdmlzdWFsaXphdGlvbi4NCg0KKipMZWdlbmRzOiBEZWNvZGluZyB0aGUgUmVwcmVzZW50YXRpb24qKg0KDQpMZWdlbmRzIGFjdCBhcyB0aGUga2V5IHRvIHVuZGVyc3RhbmRpbmcgdGhlIHN5bWJvbHMsIGNvbG9ycywgb3IgcGF0dGVybnMgdXNlZCBpbiBhIHZpc3VhbGl6YXRpb24uIFBhcnRpY3VsYXJseSBpbiBtdWx0aS12YXJpYWJsZSByZXByZXNlbnRhdGlvbnMsIGxlZ2VuZHMgZW5hYmxlIHZpZXdlcnMgdG8gZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIGNhdGVnb3JpZXMgb3IgZGF0YXNldHMuIEZvciBleGFtcGxlLCBhIGxpbmUgY2hhcnQgd2l0aCBtdWx0aXBsZSBjb2xvcmVkIGxpbmVzIHJlcHJlc2VudGluZyBkaWZmZXJlbnQgcmVnaW9ucyByZWxpZXMgb24gYSBsZWdlbmQgdG8gY2xhcmlmeSB0aGUgbWFwcGluZyBiZXR3ZWVuIGNvbG9ycyBhbmQgcmVnaW9ucy4gQSB3ZWxsLWRlc2lnbmVkIGxlZ2VuZCBpcyBjb25jaXNlLCBzdHJhdGVnaWNhbGx5IHBsYWNlZCwgYW5kIGFsaWduZWQgd2l0aCB0aGUgb3ZlcmFsbCBkZXNpZ24gb2YgdGhlIHZpc3VhbGl6YXRpb24uDQoNCioqQ29sb3I6IEVuaGFuY2luZyBDb21tdW5pY2F0aW9uKioNCg0KQ29sb3IgaXMgb25lIG9mIHRoZSBtb3N0IHBvd2VyZnVsIHRvb2xzIGluIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uLCBjYXBhYmxlIG9mIGNvbnZleWluZyBpbmZvcm1hdGlvbiwgZHJhd2luZyBhdHRlbnRpb24sIGFuZCBldm9raW5nIGVtb3Rpb25zLiBTdHJhdGVnaWMgdXNlIG9mIGNvbG9yIGVuaGFuY2VzIHRoZSBpbnRlcnByZXRhYmlsaXR5IG9mIGRhdGEgYnkgZGlmZmVyZW50aWF0aW5nIGNhdGVnb3JpZXMsIGhpZ2hsaWdodGluZyB0cmVuZHMsIG9yIGVtcGhhc2l6aW5nIGNyaXRpY2FsIHBvaW50cy4gRm9yIGV4YW1wbGUsIGEgaGVhdG1hcCB1c2VzIGNvbG9yIGdyYWRpZW50cyB0byByZXByZXNlbnQgaW50ZW5zaXR5IG9yIG1hZ25pdHVkZSwgd2hpbGUgY29udHJhc3RpbmcgY29sb3JzIGluIGEgYmFyIGNoYXJ0IGRpc3Rpbmd1aXNoIGJldHdlZW4gY2F0ZWdvcmllcy4gQWNjZXNzaWJpbGl0eSBjb25zaWRlcmF0aW9ucywgc3VjaCBhcyB1c2luZyBjb2xvcmJsaW5kLWZyaWVuZGx5IHBhbGV0dGVzLCBhcmUgZXNzZW50aWFsIHRvIGVuc3VyZSBpbmNsdXNpdml0eSBhbmQgYnJvYWQgdXNhYmlsaXR5Lg0KDQoqKlRpdGxlcywgQ2FwdGlvbnMsIGFuZCBBbm5vdGF0aW9uczogUHJvdmlkaW5nIENvbnRleHQqKg0KDQpUaXRsZXMsIGNhcHRpb25zLCBhbmQgYW5ub3RhdGlvbnMgYXJlIHRleHR1YWwgY29tcG9uZW50cyB0aGF0IG9mZmVyIGNvbnRleHQsIHN1bW1hcml6ZSBpbnNpZ2h0cywgb3IgZXhwbGFpbiBzcGVjaWZpYyBhc3BlY3RzIG9mIHRoZSB2aXN1YWxpemF0aW9uLiBUaGUgdGl0bGUgYWN0cyBhcyB0aGUgaGVhZGxpbmUsIHN1Y2NpbmN0bHkgc3RhdGluZyB0aGUgcHVycG9zZSBvciB0YWtlYXdheSBvZiB0aGUgdmlzdWFsLiBDYXB0aW9ucyBwcm92aWRlIHN1cHBsZW1lbnRhcnkgaW5mb3JtYXRpb24sIHN1Y2ggYXMgZGF0YSBzb3VyY2VzIG9yIG1ldGhvZG9sb2d5LCB3aGlsZSBhbm5vdGF0aW9ucyBoaWdobGlnaHQga2V5IHRyZW5kcywgb3V0bGllcnMsIG9yIGJlbmNobWFya3MuIFRvZ2V0aGVyLCB0aGVzZSBjb21wb25lbnRzIGd1aWRlIHZpZXdlcnMgdGhyb3VnaCB0aGUgZGF0YSBzdG9yeSBhbmQgcmVpbmZvcmNlIHRoZSBpbnRlbmRlZCBtZXNzYWdlLg0KDQpcDQoNCkluIHN1bW1hcnksIGVhY2ggZ3JhcGhpY2FsIGNvbXBvbmVudCBjb250cmlidXRlcyB1bmlxdWVseSB0byB0aGUgY2xhcml0eSBhbmQgZnVuY3Rpb25hbGl0eSBvZiBhIHZpc3VhbGl6YXRpb24uIFdoZW4gdGhvdWdodGZ1bGx5IGludGVncmF0ZWQsIHRoZXNlIGVsZW1lbnRzIHdvcmsgdG9nZXRoZXIgdG8gY3JlYXRlIGEgZGVzaWduIHRoYXQgaXMgaW50dWl0aXZlLCB2aXN1YWxseSBhcHBlYWxpbmcsIGFuZCBlZmZlY3RpdmUgaW4gY29tbXVuaWNhdGluZyBpdHMgbWVzc2FnZS4NCg0KQW4gaW50dWl0aXZlIHZpc3VhbGl6YXRpb24gZW5zdXJlcyB0aGUgYXVkaWVuY2UgY2FuIGludGVycHJldCBpbmZvcm1hdGlvbiBlYXNpbHkgYW5kIGFjY3VyYXRlbHksIHdpdGhvdXQgY29uZnVzaW9uIG9yIGFtYmlndWl0eS4gQWVzdGhldGljcywgc3VjaCBhcyBoYXJtb25pb3VzIGNvbG9yIHNjaGVtZXMgYW5kIGNsZWFuIGxheW91dHMsIGZ1cnRoZXIgZW5oYW5jZSBlbmdhZ2VtZW50IGFuZCBjb21wcmVoZW5zaW9uLiBCeSBiYWxhbmNpbmcgY2xhcml0eSwgZnVuY3Rpb25hbGl0eSwgYW5kIGRlc2lnbiwgYSB3ZWxsLWNyYWZ0ZWQgdmlzdWFsaXphdGlvbiB0cmFuc2Zvcm1zIGRhdGEgaW50byBtZWFuaW5nZnVsIGluc2lnaHRzLCBlbXBvd2VyaW5nIHN0b3J5dGVsbGluZy4NCg0KDQojIyBQdXJwb3NlcyBvZiBWaXogUmVwcmVzZW50YXRpb24NCg0KVmlzdWFsIHJlcHJlc2VudGF0aW9uIHBsYXlzIGEgcGl2b3RhbCByb2xlIGluIHRoZSBmaWVsZCBvZiBkYXRhIHNjaWVuY2UsIGVzcGVjaWFsbHkgZHVyaW5nIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkgYW5kIGZlYXR1cmUgZW5naW5lZXJpbmcuIFRoZXNlIHR3byBwaGFzZXMgYXJlIGNyaXRpY2FsIGZvciB1bmRlcnN0YW5kaW5nIGRhdGFzZXRzLCBpZGVudGlmeWluZyBwYXR0ZXJucywgYW5kIHByZXBhcmluZyBkYXRhIGZvciBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4gVGhlIHVzZSBvZiB2aXN1YWxpemF0aW9ucyBub3Qgb25seSBzaW1wbGlmaWVzIGNvbXBsZXggZGF0YSBidXQgYWxzbyBlbmFibGVzIGFuYWx5c3RzIHRvIHVuY292ZXIgaW5zaWdodHMgdGhhdCBtYXkgYmUgb2JzY3VyZWQgaW4gcmF3IG51bWJlcnMuIEluIHRoaXMgY29udGV4dCwgdW5kZXJzdGFuZGluZyB0aGUgcHVycG9zZXMgYW5kIHR5cGVzIG9mIHZpc3VhbCByZXByZXNlbnRhdGlvbiBiZWNvbWVzIGVzc2VudGlhbCBmb3IgZWZmZWN0aXZlIGFuYWx5c2lzIGFuZCBmZWF0dXJlIGRldmVsb3BtZW50Lg0KDQpUaGUgcHVycG9zZXMgb2YgdmlzdWFsIHJlcHJlc2VudGF0aW9uIGNhbiBiZSBicm9hZGx5IGNhdGVnb3JpemVkIGludG8gdGhlIGZvbGxvd2luZyBrZXkgYXJlYXMuDQoNCg0KKipVbmRlcnN0YW5kaW5nIERhdGEgRGlzdHJpYnV0aW9uKioNCg0KVmlzdWFsaXphdGlvbnMgaGVscCBkYXRhIHNjaWVudGlzdHMgZXhhbWluZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGluZGl2aWR1YWwgdmFyaWFibGVzLiBDaGFydHMgc3VjaCBhcyBoaXN0b2dyYW1zLCBkZW5zaXR5IHBsb3RzLCBhbmQgYm94IHBsb3RzIGFyZSBjb21tb25seSB1c2VkIHRvIGlkZW50aWZ5IHRoZSBjZW50cmFsIHRlbmRlbmN5LCBzcHJlYWQsIHNrZXduZXNzLCBhbmQgcHJlc2VuY2Ugb2Ygb3V0bGllcnMuIEZvciBpbnN0YW5jZSwgYSBoaXN0b2dyYW0gbWF5IHJldmVhbCB0aGF0IGEgdmFyaWFibGUgaXMgcmlnaHQtc2tld2VkLCBzdWdnZXN0aW5nIHRoZSBuZWVkIGZvciB0cmFuc2Zvcm1hdGlvbiB0byBub3JtYWxpemUgdGhlIGRhdGEuDQoNCioqSWRlbnRpZnlpbmcgUGF0dGVybnMgYW5kIFJlbGF0aW9uc2hpcHMqKg0KDQpEdXJpbmcgRURBLCB2aXN1YWxpemF0aW9ucyByZXZlYWwgcGF0dGVybnMgYW5kIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMuIFNjYXR0ZXIgcGxvdHMsIHBhaXIgcGxvdHMsIGFuZCBoZWF0bWFwcyBhbGxvdyBhbmFseXN0cyB0byBleHBsb3JlIGNvcnJlbGF0aW9ucyBhbmQgZGVwZW5kZW5jaWVzLiBGb3IgZXhhbXBsZSwgYSBzY2F0dGVyIHBsb3QgbWlnaHQgc2hvdyBhIHN0cm9uZyBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFkdmVydGlzaW5nIHNwZW5kIGFuZCBzYWxlcywgZ3VpZGluZyBmZWF0dXJlIHNlbGVjdGlvbiBmb3IgcHJlZGljdGl2ZSBtb2RlbHMuDQoNCioqRGV0ZWN0aW5nIEFub21hbGllcyBhbmQgT3V0bGllcnMqKg0KDQpPdXRsaWVycyBjYW4gc2lnbmlmaWNhbnRseSBpbXBhY3QgbWFjaGluZSBsZWFybmluZyBtb2RlbHMsIG1ha2luZyB0aGVpciBkZXRlY3Rpb24gY3J1Y2lhbC4gVmlzdWFsIHRvb2xzIGxpa2UgYm94IHBsb3RzIGFuZCBzY2F0dGVyIHBsb3RzIGVuYWJsZSBhbmFseXN0cyB0byBwaW5wb2ludCBhbm9tYWxpZXMgZWZmZWN0aXZlbHkuIEZvciBpbnN0YW5jZSwgYSBib3ggcGxvdCBvZiB0cmFuc2FjdGlvbiBhbW91bnRzIGluIGEgZGF0YXNldCBtYXkgaGlnaGxpZ2h0IHVudXN1YWxseSBoaWdoIHZhbHVlcyBpbmRpY2F0aXZlIG9mIHBvdGVudGlhbCBmcmF1ZC4NCg0KKipWYWxpZGF0aW5nIERhdGEgUXVhbGl0eSoqDQoNClZpc3VhbGl6YXRpb25zIGhlbHAgYXNzZXNzIGRhdGEgcXVhbGl0eSBieSBoaWdobGlnaHRpbmcgbWlzc2luZyB2YWx1ZXMsIGR1cGxpY2F0ZXMsIG9yIGluY29uc2lzdGVudCBwYXR0ZXJucy4gSGVhdG1hcHMgb3IgYmFyIHBsb3RzIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSBtaXNzaW5nIGRhdGEgZGlzdHJpYnV0aW9ucywgZ3VpZGluZyB0aGUgY2hvaWNlIG9mIGltcHV0YXRpb24gc3RyYXRlZ2llcy4NCg0KKipHdWlkaW5nIEZlYXR1cmUgRW5naW5lZXJpbmcqKg0KDQpGZWF0dXJlIGVuZ2luZWVyaW5nIGludm9sdmVzIGNyZWF0aW5nIG9yIHRyYW5zZm9ybWluZyBmZWF0dXJlcyB0byBpbXByb3ZlIG1vZGVsIHBlcmZvcm1hbmNlLiBWaXN1YWxpemF0aW9ucyBzdWNoIGFzIGNvcnJlbGF0aW9uIG1hdHJpY2VzIGFuZCBkZWNpc2lvbiBib3VuZGFyeSBwbG90cyBjYW4gaW5zcGlyZSBuZXcgZmVhdHVyZSBjb21iaW5hdGlvbnMgb3IgdHJhbnNmb3JtYXRpb25zLiBGb3IgZXhhbXBsZSwgYSBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gdHdvIHZhcmlhYmxlcyBtaWdodCBzdWdnZXN0IGNyZWF0aW5nIGFuIGludGVyYWN0aW9uIHRlcm0sIHdoaWxlIHBhdHRlcm5zIGluIGEgc2NhdHRlciBwbG90IGNvdWxkIGluZGljYXRlIHRoZSBuZWVkIGZvciBmZWF0dXJlIHNjYWxpbmcuDQoNCioqQ29tbXVuaWNhdGluZyBJbnNpZ2h0cyoqDQoNClZpc3VhbGl6YXRpb25zIGFyZSBhbiBlZmZlY3RpdmUgd2F5IHRvIGNvbW11bmljYXRlIGZpbmRpbmdzIGZyb20gRURBIGFuZCBmZWF0dXJlIGVuZ2luZWVyaW5nIHRvIHN0YWtlaG9sZGVycy4gQ2xlYXIsIGNvbmNpc2UgZ3JhcGhzIGhlbHAgYnJpZGdlIHRoZSBnYXAgYmV0d2VlbiB0ZWNobmljYWwgYW5hbHlzaXMgYW5kIGJ1c2luZXNzIGRlY2lzaW9ucy4gRm9yIGluc3RhbmNlLCBhIGJhciBjaGFydCBpbGx1c3RyYXRpbmcgdGhlIGltcG9ydGFuY2Ugb2Ygc3BlY2lmaWMgZmVhdHVyZXMgY2FuIGd1aWRlIGRpc2N1c3Npb25zIG9uIHJlc291cmNlIGFsbG9jYXRpb24gb3Igc3RyYXRlZ2ljIGZvY3VzLg0KDQoNCiMjIFR5cGVzIG9mIFZpc3VhbCBSZXByZXNlbnRhdGlvbg0KDQpWaXN1YWwgcmVwcmVzZW50YXRpb24gaXMgYSBjb3JuZXJzdG9uZSBvZiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpIGFuZCBmZWF0dXJlIGVuZ2luZWVyaW5nLCBwcm92aWRpbmcgZGF0YSBzY2llbnRpc3RzIHdpdGggdG9vbHMgdG8gdW5kZXJzdGFuZCBkYXRhLCBpZGVudGlmeSBwYXR0ZXJucywgYW5kIHByZXBhcmUgZmVhdHVyZXMgZm9yIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLiBEaWZmZXJlbnQgdHlwZXMgb2YgdmlzdWFsaXphdGlvbnMgc2VydmUgc3BlY2lmaWMgcHVycG9zZXMsIGZyb20gdW5kZXJzdGFuZGluZyB2YXJpYWJsZSBkaXN0cmlidXRpb25zIHRvIGRldGVjdGluZyBvdXRsaWVycyBhbmQgcmVsYXRpb25zaGlwcy4gQmVsb3cgYXJlIHRoZSBwcmltYXJ5IHR5cGVzIG9mIHZpc3VhbCByZXByZXNlbnRhdGlvbiB1c2VkIGluIHRoZXNlIHN0YWdlcyBvZiBkYXRhIHNjaWVuY2UuDQoNCg0KKipVbml2YXJpYXRlIFZpc3VhbGl6YXRpb25zKioNCg0KVW5pdmFyaWF0ZSB2aXN1YWxpemF0aW9ucyBmb2N1cyBvbiB0aGUgZGlzdHJpYnV0aW9uIGFuZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgYSBzaW5nbGUgdmFyaWFibGUsIGhlbHBpbmcgdG8gYXNzZXNzIGl0cyBjZW50cmFsIHRlbmRlbmN5LCB2YXJpYWJpbGl0eSwgYW5kIHBvdGVudGlhbCBvdXRsaWVycy4gRGVwZW5kaW5nIG9uIHRoZSB0eXBlcyBmZWF0dXJlIHZhcmlhYmxlcywgdGhlIGZvbGxvd2luZyBjaGFydHMgYXJlIGNvbW1vbmx5IHVzZWQgaW4gcHJhY3RpY2UNCg0KKiAqKkhpc3RvZ3JhbXMqKjogVXNlZCB0byBkaXNwbGF5IHRoZSBmcmVxdWVuY3kgZGlzdHJpYnV0aW9uIG9mIG51bWVyaWNhbCB2YXJpYWJsZXMsIHJldmVhbGluZyBza2V3bmVzcywgbW9kYWxpdHksIG9yIGdhcHMgaW4gdGhlIGRhdGEuDQoqICoqQm94IFBsb3RzKio6IEhpZ2hsaWdodCB0aGUgc3ByZWFkLCBtZWRpYW4sIHF1YXJ0aWxlcywgYW5kIHBvdGVudGlhbCBvdXRsaWVycywgcHJvdmlkaW5nIGEgY29uY2lzZSBzdW1tYXJ5IG9mIHRoZSB2YXJpYWJsZSdzIGRpc3RyaWJ1dGlvbi4NCiogKipEZW5zaXR5IFBsb3RzKio6IFNtb290aGVuZWQgdmVyc2lvbnMgb2YgaGlzdG9ncmFtcyB0aGF0IHNob3cgdGhlIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24sIG1ha2luZyB0aGVtIHVzZWZ1bCBmb3IgY29tcGFyaW5nIGRpc3RyaWJ1dGlvbnMuDQoqICoqQmFyIENoYXJ0cyoqOiBJZGVhbCBmb3IgdmlzdWFsaXppbmcgdGhlIGZyZXF1ZW5jeSBvZiBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIGdpdmluZyBhbiBvdmVydmlldyBvZiB0aGUgZGF0YXNldCBjb21wb3NpdGlvbi4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuY2FwPSJDb21wYXJpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0d28gbnVtZXJpY2FsIHZhcmlhYmxlcyJ9DQojIFNpbXVsYXRpbmcgc2FtcGxlIGRhdGENCmRhdGEgPC0gZGF0YS5mcmFtZShDYXRlZ29yeSA9IHJlcChjKCJBIiwgIkIiKSwgZWFjaCA9IDUwKSwNCiAgICAgICAgICAgICAgICAgICBWYWx1ZSA9IGMocm5vcm0oNTAsIG1lYW4gPSA1KSwgcm5vcm0oNTAsIG1lYW4gPSA3KSkpDQoNCmJveHBsb3QoZGF0YSRWYWx1ZSB+IGRhdGEkQ2F0ZWdvcnksDQogICAgICAgIHhsYWI9IkNhdGVnb3J5IiwNCiAgICAgICAgeWxhYj0iVmFsdWUiLA0KICAgICAgICBjb2wgPSBjKCJza3libHVlIiwgInB1cnBsZSIpLA0KICAgICAgICBtYWluPSJDb21wYXJpc29uIGJldHdlZW4gdHdvIERpc3RyaWJ1dGlvbnMiLA0KICAgICAgICBjZXgubWFpbiA9IDEuMSwNCiAgICAgICAgY29sLm1haW4gPSAibmF2eSIpDQoNCmBgYA0KDQoqKkJpdmFyaWF0ZSBWaXN1YWxpemF0aW9ucyoqDQoNCkJpdmFyaWF0ZSB2aXN1YWxpemF0aW9ucyBleHBsb3JlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0d28gdmFyaWFibGVzLCB1bmNvdmVyaW5nIGRlcGVuZGVuY2llcyBvciBjb3JyZWxhdGlvbnMgdGhhdCBtYXkgZ3VpZGUgZmVhdHVyZSBzZWxlY3Rpb24gb3IgdHJhbnNmb3JtYXRpb24uDQoNCiogKipTY2F0dGVyIFBsb3RzKio6IFVzZWQgdG8gaWRlbnRpZnkgY29ycmVsYXRpb25zLCBjbHVzdGVycywgb3IgdHJlbmRzIGJldHdlZW4gdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMuDQoqICoqTGluZSBDaGFydHMqKjogU3VpdGFibGUgZm9yIHRlbXBvcmFsIGRhdGEsIHNob3dpbmcgdHJlbmRzIG9yIGNoYW5nZXMgb3ZlciB0aW1lLg0KKiAqKkdyb3VwZWQgQmFyIENoYXJ0cyoqOiBVc2VmdWwgZm9yIGNvbXBhcmluZyBjYXRlZ29yaWVzIGFjcm9zcyBtdWx0aXBsZSBncm91cHMgb3Igc3ViZ3JvdXBzLg0KKiAqKkhlYXRtYXBzKio6IERpc3BsYXkgY29ycmVsYXRpb25zIGJldHdlZW4gdmFyaWFibGVzIHVzaW5nIGNvbG9yIGdyYWRpZW50cywgbWFraW5nIHRoZW0gYSBwb3B1bGFyIGNob2ljZSBmb3IgdmlzdWFsaXppbmcgY29ycmVsYXRpb24gbWF0cmljZXMuDQoNCg0KYGBge3IgIGZpZy5jYXA9IkNvcnJlbGF0aW9uIGJldHdlZW4gc2VwYWwgbGVuZ3RoIGFuZCBwZXRhbCBsZW5ndGguIn0NCiMgTG9hZCBpcmlzIGRhdGFzZXQNCmRhdGEoaXJpcykNCm1yZyA8LSBsaXN0KGwgPSA1MCwgciA9IDUwLCBiID0gNTAsIHQgPSA1MCwgcGFkID0gMjApDQojIw0KcGFsIDwtIGMoIiMxYjllNzciLCAiI2Q5NWYwMiIsICIjNzU3MGIzIikNCnBhbCA8LSBzZXROYW1lcyhwYWwsIGMoInZpcmdpbmljYSIsICJzZXRvc2EiLCAidmVyc2ljb2xvciIpKQ0KIw0KZmlnIDwtIHBsb3RfbHkoZGF0YSA9IGlyaXMsIHggPSB+U2VwYWwuTGVuZ3RoLCB5ID0gflBldGFsLkxlbmd0aCwgDQogICAgICAgICAgICAgICBjb2xvciA9IH5TcGVjaWVzLCBjb2xvcnMgPSBwYWwsDQogICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gflNlcGFsLkxlbmd0aCoyKSkgJT4lIA0KICAgICAgIGxheW91dCh0aXRsZSA9ICdDb3JyZWxhdGlvbiBiZXR3ZWVuIFBldGFsIExlbmd0aCBhbmQgU2VwYWwgTGVuZ3RoJywgDQogICAgICAgICAgICAgIHBsb3RfYmdjb2xvciA9ICJ3aGl0ZSIsIA0KICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnU2VwYWwgTGVuZ3RoIChjbSknKSwgDQogICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnU2VwYWwgV2lkdGggKGNtKScpLCANCiAgICAgICAgICAgICAgbGVnZW5kID0gbGlzdCh4ID0gMC43LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gMC4xLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPWxpc3QodGV4dD0nPGI+IFNwZWNpZXMgb2YgSXJpcyA8L2I+JykpLA0KICAgICAgICAgICAgICBtYXJnaW4gPSBtcmcpDQpmaWcNCmBgYA0KDQogDQoqKk11bHRpdmFyaWF0ZSBWaXN1YWxpemF0aW9ucyoqDQoNCk11bHRpdmFyaWF0ZSB2aXN1YWxpemF0aW9ucyBwcm92aWRlIGluc2lnaHRzIGludG8gcmVsYXRpb25zaGlwcyBhbW9uZyB0aHJlZSBvciBtb3JlIHZhcmlhYmxlcywgaGVscGluZyB0byBpZGVudGlmeSBjb21wbGV4IGludGVyYWN0aW9ucy4NCg0KKiAqKlBhaXIgUGxvdHMqKjogT2ZmZXIgYSBncmlkIG9mIHNjYXR0ZXIgcGxvdHMgZm9yIGFsbCBwYWlycyBvZiB2YXJpYWJsZXMsIGFsb25nIHdpdGggaGlzdG9ncmFtcyBmb3IgaW5kaXZpZHVhbCB2YXJpYWJsZXMsIGVuYWJsaW5nIGEgcXVpY2sgb3ZlcnZpZXcgb2YgcmVsYXRpb25zaGlwcy4NCiogKipJbnRlcmFjdGl2ZSAzRCBTY2F0dGVyIFBsb3RzKio6IFZpc3VhbGl6ZSByZWxhdGlvbnNoaXBzIGluIHRocmVlIGRpbWVuc2lvbnMsIGFkZGluZyBkZXB0aCB0byB0aGUgYW5hbHlzaXMgb2YgZmVhdHVyZSBpbnRlcmFjdGlvbnMuICoqY2F1dGlvbioqIC0gc3RhdGljIDNEIHNjYXR0ZXIgc2hvdWxkIGJlIGF2b2lkZWQuDQoqICoqUGFyYWxsZWwgQ29vcmRpbmF0ZXMqKjogUmVwcmVzZW50IG11bHRpcGxlIHZhcmlhYmxlcyBmb3IgZWFjaCBkYXRhIHBvaW50IGFzIGNvbm5lY3RlZCBsaW5lIHNlZ21lbnRzLCB1c2VmdWwgZm9yIHZpc3VhbGl6aW5nIHRyZW5kcyBpbiBoaWdoLWRpbWVuc2lvbmFsIGRhdGEuDQoqICoqQnViYmxlIENoYXJ0cyoqOiBBZGQgYSB0aGlyZCBkaW1lbnNpb24gdG8gc2NhdHRlciBwbG90cyB0aHJvdWdoIGJ1YmJsZSBzaXplLCBvZnRlbiB1c2VkIHRvIGluY29ycG9yYXRlIGEgd2VpZ2h0IG9yIG1hZ25pdHVkZSBmYWN0b3IuDQoNCg0KYGBge3IgZmlnLmNhcD0iUGFpcndpc2Ugc2NhdHRlciBwbG90IG9mIG51bWVyaWNhbCB2YXJpYWJsZXMgaW4gdGhlIGlyaXMgZGF0YSJ9DQpuYW1lcyhpcmlzKSA9IGMoIlNlcGFsLkwiLCAiU2VwYWwuVyIsICAiUGV0YWwuTCIsICJQZXRhbC5XIiwgIlNwZWNpZXMiICkNCnAgPC0gZ2dwYWlycyhpcmlzLCBjb2x1bW5zPTE6NCwgDQogICAgICAgICAgICAgYWVzKGNvbG9yID0gZmFjdG9yKFNwZWNpZXMpLCBhbHBoYSA9IDAuNSksIA0KICAgICAgICAgICAgIHVwcGVyID0gbGlzdChjb250aW51b3VzID0gd3JhcCgiY29yIiwgc2l6ZSA9IDMpKSwNCiAgICAgICAgICAgICBtYXJnaW4gPSBsaXN0KGwgPSA1MCwgciA9IDUwLCBiID0gNTAsIHQgPSA1MCwgcGFkID0gMjApKSArDQogICAgICBnZ3RpdGxlKCJQYWlyd2lzZWQgU2NhdHRlciBQbG90IG9mIElyaXMgRGF0YSIpICsgDQogICAgICB0aGVtZSgNCiAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSwgICAgIyBDZW50ZXItYWxpZ24gdGhlIHRpdGxlDQogICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLCAyLCAyLCAxKSwgImNtIikgICMgYWRqdXN0IHRoZSBtYXJnaW4gb2YgdGhlIHBsb3QNCiAgKQ0KICAjDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCg0KKipWaXN1YWxpemF0aW9ucyBmb3IgQ2F0ZWdvcmljYWwgRGF0YSoqDQoNCkNhdGVnb3JpY2FsIGRhdGEgb2Z0ZW4gcmVxdWlyZXMgZGlzdGluY3QgdHlwZXMgb2YgdmlzdWFsIHJlcHJlc2VudGF0aW9uIHRvIGhpZ2hsaWdodCBkaXN0cmlidXRpb25zIGFuZCBjb21wYXJpc29ucy4NCg0KKiAqKlN0YWNrZWQgQmFyIENoYXJ0cyoqOiBEaXNwbGF5IHRoZSBjb21wb3NpdGlvbiBvZiBjYXRlZ29yaWVzIHdpdGhpbiBzdWJncm91cHMsIGFpZGluZyBpbiB0aGUgYW5hbHlzaXMgb2YgcHJvcG9ydGlvbnMuDQoqICoqTW9zYWljIFBsb3RzKio6IFByb3ZpZGUgYSBkZXRhaWxlZCB2aWV3IG9mIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdHdvIG9yIG1vcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRocm91Z2ggcHJvcG9ydGlvbmFsIGFyZWFzLg0KKiAqKlZpb2xpbiBQbG90cyoqOiBDb21iaW5lIGJveCBwbG90cyBhbmQgZGVuc2l0eSBwbG90cyB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgbnVtZXJpY2FsIGRhdGEgYWNyb3NzIGNhdGVnb3JpZXMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmNhcD0iQW4gaWxsdXN0cmF0aXZlIGV4YW1wbGUgb2Ygc3RhY2tlZCBiYXIgY2hhcnQgdG8gY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBvbmUgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aGluIGNhdGVnb3JpZXMgb2YgdGhlIG90aGVyIHZhcmlhYmxlLiJ9DQojIEV4YW1wbGUgZGF0YXNldA0KZGF0YSA8LSBkYXRhLmZyYW1lKA0KICBDYXRlZ29yeSA9IGMoIkEiLCAiQSIsICJCIiwgIkIiLCAiQyIsICJDIiksDQogIFN1YmNhdGVnb3J5ID0gYygiWCIsICJZIiwgIlgiLCAiWSIsICJYIiwgIlkiKSwNCiAgVmFsdWUgPSBjKDEwLCAyMCwgMTUsIDI1LCAzMCwgMTApDQopDQojDQojIENyZWF0ZSB0aGUgc3RhY2tlZCBiYXIgY2hhcnQNCnBsb3QgPC0gcGxvdF9seSgNCiAgZGF0YSwNCiAgeCA9IH5DYXRlZ29yeSwNCiAgeSA9IH5WYWx1ZSwNCiAgY29sb3IgPSB+U3ViY2F0ZWdvcnksDQogIHR5cGUgPSAiYmFyIg0KICApICU+JQ0KICBsYXlvdXQoDQogICAgYmFybW9kZSA9ICJzdGFjayIsICMgU3RhY2sgdGhlIGJhcnMNCiAgICB0aXRsZSA9ICJTdGFja2VkIEJhciBDaGFydCIsDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkNhdGVnb3J5IiksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlZhbHVlIikNCiAgICkNCiMgRGlzcGxheSB0aGUgcGxvdA0KcGxvdA0KYGBgDQoNCg0KKipTcGVjaWFsaXplZCBWaXN1YWxpemF0aW9ucyBmb3IgRURBKioNCg0KQ2VydGFpbiB2aXN1YWxpemF0aW9ucyBjYXRlciBzcGVjaWZpY2FsbHkgdG8gdGhlIG5lZWRzIG9mIEVEQSwgZm9jdXNpbmcgb24gZGF0YSBxdWFsaXR5IGFuZCBzdHJ1Y3R1cmUuDQoNCiogKipNaXNzaW5nIERhdGEgSGVhdG1hcHMqKjogSGlnaGxpZ2h0IHRoZSBkaXN0cmlidXRpb24gYW5kIGZyZXF1ZW5jeSBvZiBtaXNzaW5nIHZhbHVlcyBhY3Jvc3MgdGhlIGRhdGEgc2V0LCBndWlkaW5nIGltcHV0YXRpb24gc3RyYXRlZ2llcy4NCiogKipIaXN0b2dyYW0gRmFjZXRzKio6IERpc3BsYXkgZGlzdHJpYnV0aW9ucyBvZiBhIG51bWVyaWNhbCB2YXJpYWJsZSBhY3Jvc3MgbGV2ZWxzIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUuDQoqICoqT3V0bGllciBQbG90cyoqOiBIaWdobGlnaHQgYW5vbWFsaWVzIHVzaW5nIHNjYXR0ZXIgcGxvdHMgb3Igc3BlY2lhbGl6ZWQgdmlzdWFsaXphdGlvbnMgbGlrZSBib3ggcGxvdCB3aGlza2Vycy4NCg0KYGBge3IgIGZpZy5jYXA9IkNvbXBhcmluZyB0aGUgZGlzdHJpYnV0aW9ucyBudW1lcmljYWwgdmFpYWJsZSBhY3Jvc3MgdGhlIGNhdGVnb3JpZXMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4ifQ0KIyBFeGFtcGxlIGRhdGFzZXQNCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgVmFsdWUgPSBjKHJub3JtKDEwMCwgbWVhbiA9IDUpLCBybm9ybSgxMDAsIG1lYW4gPSAxMCkpLA0KICBDYXRlZ29yeSA9IHJlcChjKCJHcm91cCAxIiwgIkdyb3VwIDIiKSwgZWFjaCA9IDEwMCkNCikNCg0KIyBDcmVhdGUgdGhlIGZhY2V0ZWQgaGlzdG9ncmFtDQpnZyA9IGdncGxvdChkYXRhLCBhZXMoeCA9IFZhbHVlLCBmaWxsID0gQ2F0ZWdvcnkpKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhjb2xvciA9IENhdGVnb3J5KSwgYmlucyA9IDIwLCBhbHBoYSA9IDAuNywgc2l6ZSA9IDEpICsNCiAgZmFjZXRfd3JhcCh+IENhdGVnb3J5LCBuY29sID0gMSkgKyAjIEZhY2V0IGJ5IENhdGVnb3J5DQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJHcm91cCAxIiA9ICJibHVlIiwgIkdyb3VwIDIiID0gImdyZWVuIikpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiR3JvdXAgMSIgPSAibGlnaHRibHVlIiwgIkdyb3VwIDIiID0gImxpZ2h0Z3JlZW4iKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBGYWNldHMgd2l0aCBDb2xvcmVkIEJvdW5kYXJpZXMiLA0KICAgIHggPSAiVmFsdWUiLA0KICAgIHkgPSAiRnJlcXVlbmN5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAjIFJlbW92ZSBsZWdlbmQgaWYgaXQncyByZWR1bmRhbnQNCiAgKQ0KZ2dwbG90bHkoZ2cpDQpgYGANCg0KDQoqKlZpc3VhbGl6YXRpb25zIGZvciBGZWF0dXJlIEVuZ2luZWVyaW5nKioNCg0KRmVhdHVyZSBlbmdpbmVlcmluZyByZWxpZXMgb24gdmlzdWFsaXphdGlvbnMgdG8gYXNzZXNzIHRoZSBlZmZlY3RpdmVuZXNzIG9mIHRyYW5zZm9ybWF0aW9ucywgY29tYmluYXRpb25zLCBhbmQgZmVhdHVyZSBpbXBvcnRhbmNlLg0KDQoqICoqQ29ycmVsYXRpb24gTWF0cmljZXMqKjogRGlzcGxheSBwYWlyd2lzZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBudW1lcmljYWwgdmFyaWFibGVzLCBoZWxwaW5nIGlkZW50aWZ5IHJlZHVuZGFudCBvciBoaWdobHkgY29ycmVsYXRlZCBmZWF0dXJlcy4NCiogKipGZWF0dXJlIEltcG9ydGFuY2UgUGxvdHMqKjogRGVyaXZlZCBmcm9tIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLCB0aGVzZSBwbG90cyByYW5rIGZlYXR1cmVzIGJhc2VkIG9uIHRoZWlyIGltcGFjdCBvbiBtb2RlbCBwZXJmb3JtYW5jZS4NCiogKipEZWNpc2lvbiBCb3VuZGFyeSBWaXN1YWxpemF0aW9ucyoqOiBTaG93IHRoZSByZWdpb25zIGNyZWF0ZWQgYnkgYSBtb2RlbOKAmXMgcHJlZGljdGlvbnMsIHVzZWZ1bCBmb3IgZXZhbHVhdGluZyBmZWF0dXJlIGNvbWJpbmF0aW9ucy4NCiogKipJbnRlcmFjdGlvbiBQbG90cyoqOiBJbGx1c3RyYXRlIHRoZSBjb21iaW5lZCBlZmZlY3Qgb2YgdHdvIG9yIG1vcmUgZmVhdHVyZXMgb24gYSB0YXJnZXQgdmFyaWFibGUsIG9mdGVuIHVzZWQgaW4gcmVncmVzc2lvbiBvciBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtcy4NCg0KYGBge3IgIGZpZy5jYXA9IkRpc3BsYXkgdGhlIGludGVyYWN0aW9uIGVmZmVjdHMgaW4gYW4gQU5PVkEgbW9kZWwifQ0KIyBFeGFtcGxlIGRhdGFzZXQNCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgRmFjdG9yMSA9IHJlcChjKCJMb3ciLCAiSGlnaCIpLCBlYWNoID0gNiksDQogIEZhY3RvcjIgPSByZXAoYygiQSIsICJCIiwgIkMiKSwgdGltZXMgPSA0KSwNCiAgUmVzcG9uc2UgPSBjKDUsIDYsIDcsIDEwLCAxMSwgMTIsIDQsIDUsIDYsIDksIDgsIDEwKQ0KKQ0KDQojIENyZWF0ZSB0aGUgaW50ZXJhY3Rpb24gcGxvdA0KaW50ZXJhY3QgPSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBGYWN0b3IyLCB5ID0gUmVzcG9uc2UsIGdyb3VwID0gRmFjdG9yMSwgY29sb3IgPSBGYWN0b3IxKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsgICMgTGluZXMgcmVwcmVzZW50aW5nIGludGVyYWN0aW9uDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgIyBQb2ludHMgZm9yIGRhdGENCiAgbGFicygNCiAgICB0aXRsZSA9ICJJbnRlcmFjdGlvbiBQbG90IiwNCiAgICB4ID0gIkZhY3RvciAyIiwNCiAgICB5ID0gIlJlc3BvbnNlIiwNCiAgICBjb2xvciA9ICJGYWN0b3IgMSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCINCiAgKQ0KZ2dwbG90bHkoaW50ZXJhY3QpDQpgYGANCg0KDQoqKkludGVyYWN0aXZlIFZpc3VhbGl6YXRpb25zKioNCg0KSW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbnMgZW5oYW5jZSBleHBsb3JhdGlvbiBieSBhbGxvd2luZyB1c2VycyB0byBtYW5pcHVsYXRlIHRoZSBkYXRhIGR5bmFtaWNhbGx5Lg0KDQoqICoqRGFzaGJvYXJkcyoqOiBDb21iaW5lIG11bHRpcGxlIHZpc3VhbGl6YXRpb25zIGludG8gYSBzaW5nbGUgaW50ZXJmYWNlLCBlbmFibGluZyBmaWx0ZXJpbmcsIHpvb21pbmcsIGFuZCBzbGljaW5nIG9mIGRhdGEgZm9yIGRlZXBlciBpbnNpZ2h0cy4NCiogKipHZW9zcGF0aWFsIE1hcHMqKjogVmlzdWFsaXplIGdlb2dyYXBoaWMgZGF0YSB3aXRoIGxheWVycyBvZiBpbnRlcmFjdGl2aXR5LCBzdWNoIGFzIHpvb21pbmcgaW50byBzcGVjaWZpYyByZWdpb25zIG9yIGRpc3BsYXlpbmcgZGV0YWlsZWQgdG9vbHRpcHMuDQoqICoqSW50ZXJhY3RpdmUgU2NhdHRlciBQbG90cyoqOiBBbGxvdyB1c2VycyB0byBob3ZlciBvdmVyIHBvaW50cyBmb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb3IgZmlsdGVyIGRhdGEgYmFzZWQgb24gdmFyaWFibGUgcmFuZ2VzLg0KDQpcDQoNCkluIHN1bW1hcnksIHRoZSBkaXZlcnNpdHkgb2YgdmlzdWFsIHJlcHJlc2VudGF0aW9uIGluIEVEQSBhbmQgZmVhdHVyZSBlbmdpbmVlcmluZyB1bmRlcnNjb3JlcyBpdHMgaW1wb3J0YW5jZSBpbiB1bmRlcnN0YW5kaW5nIGFuZCBwcmVwYXJpbmcgZGF0YSBmb3IgYW5hbHlzaXMuIEZyb20gdW5pdmFyaWF0ZSBoaXN0b2dyYW1zIHRvIGludGVyYWN0aXZlIGRhc2hib2FyZHMsIGVhY2ggdHlwZSBzZXJ2ZXMgYSB1bmlxdWUgcHVycG9zZSBpbiBleHBsb3JpbmcgcGF0dGVybnMsIGFzc2Vzc2luZyBkYXRhIHF1YWxpdHksIGFuZCBvcHRpbWl6aW5nIGZlYXR1cmVzIGZvciBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscy4gQnkgc2VsZWN0aW5nIHRoZSBhcHByb3ByaWF0ZSB2aXN1YWxpemF0aW9uIGZvciB0aGUgdGFzayBhdCBoYW5kLCBkYXRhIHNjaWVudGlzdHMgY2FuIHVuY292ZXIgaW5zaWdodHMsIGVuaGFuY2UgY29tbXVuaWNhdGlvbiwgYW5kIGRyaXZlIGJldHRlciBkZWNpc2lvbi1tYWtpbmcuDQoNCg0KIyMgTW9kZWwtYmFzZWQgVmlzdWFsaXphdGlvbg0KDQpWaXN1YWxpemF0aW9uIGlzIHZpdGFsIGluIGRhdGEgc2NpZW5jZSBhbmQgbWFjaGluZSBsZWFybmluZyAoTUwpIGJlY2F1c2UgaXQgdHJhbnNmb3JtcyBkYXRhLCByZXN1bHRzLCBhbmQgbW9kZWwgaW5zaWdodHMgaW50byBjb21wcmVoZW5zaWJsZSB2aXN1YWwgZm9ybXMsIGFpZGluZyBleHBsb3JhdGlvbiwgY29tbXVuaWNhdGlvbiwgYW5kIGRlY2lzaW9uLW1ha2luZy4gDQoNCiMjIyBNb2RlbCBQZXJmb3JtYW5jZSBFdmFsdWF0aW9uDQoNClZpc3VhbGl6YXRpb24gaXMgY3JpdGljYWwgZm9yIGV2YWx1YXRpbmcgYW5kIGNvbXBhcmluZyBtb2RlbCBwZXJmb3JtYW5jZS4NCg0KKipFeGFtcGxlKio6IFJPQyBDdXJ2ZSBmb3IgQ2xhc3NpZmljYXRpb24NCg0KYGBge3J9DQpsaWJyYXJ5KHBST0MpDQojIFNpbXVsYXRlZCBjbGFzc2lmaWNhdGlvbiByZXN1bHRzDQpJRCA9IHNhbXBsZSgxOjE1MCwgMTUwLCByZXBsYWNlID0gRkFMU0UpDQp0cnVlX2xhYmVscyA8LSBjKHJlcCgxLCA1MCksIHJlcCgwLDEwMCkpW0lEXQ0KcHJlZGljdGVkX3Byb2JzIDwtIGMocm5vcm0oNTAsIDMsIDcpLCBybm9ybSgxMDAsIDEwLDQpKVtJRF0NCiMgUGxvdCBST0MgY3VydmUNCnJvY19vYmogPC0gcm9jKHRydWVfbGFiZWxzLCBwcmVkaWN0ZWRfcHJvYnMpDQpzZW4gPSByb2Nfb2JqJHNlbnNpdGl2aXRpZXMNCnNwZSA9IHJvY19vYmokc3BlY2lmaWNpdGllcw0KIyMgDQpwYXIocHR5ID0gInMiKQ0KcGxvdCgxLXNwZSwgc2VuLCB0eXBlID0gImwiLCBsd2QgPSAyLCBsdHkgPSAxLCBjb2wgPSAiYmx1ZSIsIA0KICAgICB4bGFiID0gIjEgLSBzcGVjaWZpY2l0eSIsDQogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLA0KICAgICBtYWluID0gIlJPQyBDdXJ2ZSIpDQphYmxpbmUoMCwxLCBsdHkgPSAyLCBsd2QgPSAyLCBjb2wgPSAicmVkIikNCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBjKCJtb2RlbC1iYXNlZCBQZXJmb3JtYW5jZSIsICJSYW5kb20gR3Vlc3MiKSwgDQogICAgICAgY29sPWMoImJsdWUiLCAicmVkIiksIGx0eT0xOjIsIGx3ZCA9IDI6MSwgYnR5ID0gIm4iLCBjZXggPSAwLjgpDQpgYGANCg0KQW4gaW50ZXJhY3RpdmUgcGxvdCBwcm92aWRlcyBtb3JlIGdyYW51bGFyIGluZm9ybWF0aW9uIGFib3V0IHRoZSB1bmRlcmx5aW5nIHBsb3QuDQoNCmBgYHtyfQ0KbGlicmFyeShwUk9DKQ0KIyBTaW11bGF0ZWQgY2xhc3NpZmljYXRpb24gcmVzdWx0cw0KSUQgPSBzYW1wbGUoMToxNTAsIDE1MCwgcmVwbGFjZSA9IEZBTFNFKQ0KdHJ1ZV9sYWJlbHMgPC0gYyhyZXAoMSwgNTApLCByZXAoMCwxMDApKVtJRF0NCnByZWRpY3RlZF9wcm9icyA8LSBjKHJub3JtKDUwLCAzLCA3KSwgcm5vcm0oMTAwLCAxMCw0KSlbSURdDQojIFBsb3QgUk9DIGN1cnZlDQpyb2Nfb2JqIDwtIHJvYyh0cnVlX2xhYmVscywgcHJlZGljdGVkX3Byb2JzKQ0Kc2VuID0gcm9jX29iaiRzZW5zaXRpdml0aWVzDQpzcGUgPSByb2Nfb2JqJHNwZWNpZmljaXRpZXMNClNlblNwZSA9IGRhdGEuZnJhbWUoc2VuID0gc2VuLCBzcGUgPSBzcGUpDQojIyANCmdncm9jID0gZ2dwbG90KGRhdGEgPSBTZW5TcGUsIG1hcHBpbmcgPSBhZXMoeCA9KDEtc3BlKSwgeSA9IHNlbikpICsNCiAgICAgICAgZ2VvbV9yZWN0KGFlcyh4bWluID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgeG1heCA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgIHltaW4gPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICB5bWF4ID0gMSksDQogICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJ0cmFuc3BhcmVudCIsIA0KICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtncmF5IiwgDQogICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNCAgICMgZGVmYXVsdCBpcyAwLjUNCiAgICAgICAgICAgICAgICAgICkgKw0KICAgICAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSkgKw0KICAgICAgICAjZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc3BhbiA9IDAuMSwgY29sID0gImRhcmtibHVlIikgKyANCiAgICAgICAgI2dlb21fbGluZShjb2wgPSAiZGFya2JsdWUiKSArDQogICAgICAgIGdlb21fcGF0aChjb2wgPSAiZGFya2JsdWUiKSArDQogICAgICAgICNnZW9tX2FibGluZShjb2w9ImRhcmtyZWQiLCBsaW5ldHlwZT0iZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IDEsIHllbmQgPSAxKSwgDQogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLCBsaW5ldHlwZSA9ICJkYXNoIikgKw0KDQogICAgICAgIGdndGl0bGUoIkludGVyYWN0aXZlIFJPQyBDdXJ2ZSIpICsNCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJzZXJpZiIsICAjIEZvbnQgZmFtaWx5DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCAgICAgIyBGb250IGZhY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIm5hdnkiLCAgICAjIEZvbnQgY29sb3INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxNCwgICAgICAgICAjIEZvbnQgc2l6ZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsICAgICAgICMgSG9yaXpvbnRhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IDEsICAgICAgICAgIyBWZXJ0aWNhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDAgICAgICAgICAgIyBGb250IGFuZ2xlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLA0KICAgICAgICAgICAgICBhc3BlY3QucmF0aW8gPSAxKQ0KICAgICAgICAgICAgDQpnZ3Bsb3RseShnZ3JvYykNCg0KYGBgDQoNCg0KDQoNCiMjIyBVbmRlcnN0YW5kaW5nIE1vZGVsIFByZWRpY3Rpb25zDQoNClZpc3VhbGl6YXRpb24gZXhwbGFpbnMgaG93IG1vZGVscyBhcnJpdmUgYXQgdGhlaXIgcHJlZGljdGlvbnMsIGVzc2VudGlhbCBmb3IgdHJ1c3QgYW5kIHRyYW5zcGFyZW5jeS4NCg0KRXhhbXBsZTogRmVhdHVyZSBJbXBvcnRhbmNlIGluIFJhbmRvbSBGb3Jlc3QNCg0KYGBge3J9DQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCiMgRml0IGEgcmFuZG9tIGZvcmVzdCBtb2RlbA0KcmZfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KFNwZWNpZXMgfiAuLCBkYXRhID0gaXJpcywgaW1wb3J0YW5jZSA9IFRSVUUpDQojIFBsb3QgZmVhdHVyZSBpbXBvcnRhbmNlDQp2YXJJbXBQbG90KHJmX21vZGVsLCBtYWluID0gIkZlYXR1cmUgSW1wb3J0YW5jZSBpbiBSYW5kb20gRm9yZXN0IikNCmBgYA0KDQpgYGB7cn0NCnJmX21vZGVsJGltcG9ydGFuY2UNCmBgYA0KDQoNCg0KDQojIFIgSW50ZXJhY3RpdmUgR3JhcGhpY2FsIEZ1bmN0aW9ucw0KDQpJbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucyBoYXZlIGJlY29tZSBhbiBpbmRpc3BlbnNhYmxlIHRvb2wgaW4gbW9kZXJuIGRhdGEgc2NpZW5jZS4gV2hldGhlciB5b3UncmUgcHJlcGFyaW5nIGEgcmVwb3J0LCBhIHByZXNlbnRhdGlvbiwgb3IgYSBkYXNoYm9hcmQsIHByb3ZpZGluZyBpbnRlcmFjdGl2ZSBmaWd1cmVzIGNhbiBtYWtlIHRoZSBkYXRhIG1vcmUgZW5nYWdpbmcsIGluc2lnaHRmdWwsIGFuZCBhY2Nlc3NpYmxlLiBJbiBSLCBzZXZlcmFsIHBvd2VyZnVsIHBhY2thZ2VzIGFsbG93IHlvdSB0byBjcmVhdGUgZHluYW1pYywgaW50ZXJhY3RpdmUgcGxvdHMuIEluIHRoZSBwcmV2aW91cyBzZWN0aW9uLCBJIHVzZWQgYGdncGFpcnNgLCBgcGxvdF9seWAsIGBnZ3Bsb3RseWAsIGFuZCBgbGVhZmxldGAgdG8gaWxsdXN0cmF0ZSB2YXJpb3VzIFIgcGxvdHMuIEluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBjb21wYXJlIHNvbWUgcG9wdWxhciBwYWNrYWdlcyBmb3IgaW50ZXJhY3RpdmUgZGF0YSB2aXN1YWxpemF0aW9ucyBpbiBSLCBpbmNsdWRpbmcgYGdnaXJhcGhgLCBgcGxvdGx5YCwgYGdncGxvdGx5YCwgYW5kIGBoaWdoY2hhcnRlcmAuIFRoZXNlIGxpYnJhcmllcyBhcmUgb2Z0ZW4gdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoZSAqKnVtYnJlbGxhLXBhY2thZ2UqKiBgdGlkeXZlcnNlYCBmb3IgZGF0YSBtYW5pcHVsYXRpb24uDQoNCldlIHdpbGwgdXNlIHRoZXNlIGxpYnJhcmllcyB0byB2aXN1YWxpemUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIG51bWVyaWNhbCB2YXJpYWJsZXMgYW5kIHRoZSBkaXN0cmlidXRpb24gb2YgbnVtZXJpY2FsIHZhcmlhYmxlLiBUaGUgZXhhbXBsZXMgYXJlIGJhc2VkIG9uIHRoZSBwb3B1bGFyIGBQYWxtZXIgQXJjaGlwZWxhZ28gKEFudGFyY3RpY2EpIHBlbmd1aW4gZGF0YXNldGAgd2hpY2ggaXMgc2ltaWxhciB0byB0aGUgd2VsbC1rbm93biBgaXJpc2AuIEl0IGlzIGEgZ3JlYXQgaW50cm9kdWN0b3J5IGRhdGEgc2V0IGZvciBkYXRhIGV4cGxvcmF0aW9uIGFuZCB2aXN1YWxpemF0aW9uLiANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlRocmVlIFBhbG1lciBBcmNoaXBlbGFnbyAoQW50YXJjdGljYSkgcGVuZ3VpbnMifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL3Blbmd1aW5JbWFnZS5qcGciKQ0KYGBgDQoNCkEgY29weSBvZiB0aGUgZGF0YSB3aXRoIG1pc3NpbmcgdmFsdWVzIGRlbGV0ZWQgaXMgYXZhaWxhYmxlIGF0IDxodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTIvdzAyL3Blbmd1aW4uY3N2Pi4gDQoNCmBgYHtyfQ0KcGVuZ3VpbnMgPSByZWFkLmNzdigiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUyL3cwMi9wZW5ndWluLmNzdiIpDQpgYGANCg0KVGhlIHZhcmlhYmxlcyBhcmUgZGVzY3JpYmVkIGJlbG93Og0KDQoqICoqc3BlY2llcyoqIGEgZmFjdG9yIGRlbm90aW5nIHBlbmd1aW4gc3BlY2llcyAoQWTDqWxpZSwgQ2hpbnN0cmFwIGFuZCBHZW50b28pDQoqICoqaXNsYW5kKiogYSBmYWN0b3IgZGVub3RpbmcgaXNsYW5kIGluIFBhbG1lciBBcmNoaXBlbGFnbywgQW50YXJjdGljYSAoQmlzY29lLCBEcmVhbSBvciBUb3JnZXJzZW4pDQoqICoqYmlsbF9sZW5ndGhfbW0qKiBhIG51bWJlciBkZW5vdGluZyBiaWxsIGxlbmd0aCAobWlsbGltZXRlcnMpDQoqICoqYmlsbF9kZXB0aF9tbSoqIGEgbnVtYmVyIGRlbm90aW5nIGJpbGwgZGVwdGggKG1pbGxpbWV0ZXJzKQ0KKiAqKmZsaXBwZXJfbGVuZ3RoX21tKiogYW4gaW50ZWdlciBkZW5vdGluZyBmbGlwcGVyIGxlbmd0aCAobWlsbGltZXRlcnMpDQoqICoqYm9keV9tYXNzX2cqKiBhbiBpbnRlZ2VyIGRlbm90aW5nIGJvZHkgbWFzcyAoZ3JhbXMpDQoqICoqc2V4KiogYSBmYWN0b3IgZGVub3RpbmcgcGVuZ3VpbiBzZXggKGZlbWFsZSwgbWFsZSkNCiogKip5ZWFyKiogYW4gaW50ZWdlciBkZW5vdGluZyB0aGUgc3R1ZHkgeWVhciAoMjAwNywgMjAwOCwgb3IgMjAwOSkNCg0KIyMgVmlzdWFsaXppbmcgUmVsYXRpb25zaGlwIA0KDQpUd28gb2YgdGhlIDggZmVhdHVyZSB2YXJpYWJsZXMgKipiaWxsX2xlbmd0aF9tbSoqIGFuZCAqKmJpbGxfZGVwdGhfbW0qKiB3aWxsIGJlIHVzZWQgdG8gZGVtb25zdHJhdGUgdGhlIGludGVyYWN0aXZlIHBsb3RzIHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlbSB3aXRoIGRpZmZlcmVudCBSIGdyYXBoaWNhbCBmdW5jdGlvbnMuDQoNCioqZ2dpcmFwaCoqIA0KDQpUaGUgaW50ZXJhY3RpdmUgZ3JhcGhpY2FsIGZ1bmN0aW9uIGBnZ2lyYXBoKClgIGlzIGEgd3JhcHBlciBvZiAqKmdncGxvdCgpKiouIFdlIGZpcnN0IG1ha2UgYSBnZ3Bsb3QgYW5kIHRoZW4gY2FsbCBgZ2dpcmFwaCgpYCB0byBhZGQgaW50ZXJhY3RpdmUgZmVhdHVyZSB0byB0aGUgZ2dwbG90Lg0KDQoNCmBgYHtyfQ0KZmlnX2Z1bGw9IHBlbmd1aW5zICU+JSANCiAgICAgIGdncGxvdChhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgICAgeSA9IGJpbGxfZGVwdGhfbW0sIA0KICAgICAgICAgICAgICAgICBjb2xvciA9IHNwZWNpZXMsIA0KICAgICAgICAgICAgICAgICBzaGFwZSA9IHNwZWNpZXMpKSArIA0KICAgICAgZ2VvbV9wb2ludF9pbnRlcmFjdGl2ZSggDQogICAgICAgICAgICAgICAgYWVzKHRvb2x0aXAgPSBwYXN0ZSgiYmlsbF9sZW5ndGhfbW06IiwgYmlsbF9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj4iLCAiYmlsbF9kZXB0aF9tbToiLCBiaWxsX2RlcHRoX21tKSksIA0KICAgICAgICAgICAgICAgICAgICBzaXplID0gMikgKyANCiAgICAgIGdlb21fc21vb3RoX2ludGVyYWN0aXZlKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSAieSB+IHgiKSArDQogICAgICBsYWJzKHggPSAiQmlsbCBsZW5ndGggKG1tKSIsIA0KICAgICAgICAgICB5ID0gIkJpbGwgd2lkdGggKG1tKSIsIA0KICAgICAgICAgICB0aXRsZSA9ICJCaWxsIGxlbmd0aCB2cy4gYmlsbCB3aWR0aCIsIA0KICAgICAgICAgICBzdWJ0aXRsZSA9ICJVc2luZyB0aGUgZ2dpcmFwaCBwYWNrYWdlIiwNCiAgICAgICAgICAgY29sb3IgPSAiU3BlY2llcyIsIHNoYXBlID0gIlNwZWNpZXMiKSArDQogICAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gInNlcmlmIiwgICMgRm9udCBmYW1pbHkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCAgICAgICAgICMgRm9udCBmYWNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIsICAgICAgICAjIEZvbnQgY29sb3INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTQsICAgICAgICAgICAgICMgRm9udCBzaXplDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsICAgICAgICAgICAjIEhvcml6b250YWwgYWRqdXN0bWVudA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMSwgICAgICAgICAgICAgIyBWZXJ0aWNhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSAwLCAgICAgICAgICAgICAjIEZvbnQgYW5nbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0ID0gMSksICAgICAgICMgTGluZSBzcGFjaW5nDQogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoICAgICAgICAgICAgICAgICAgICAjIFN1YnRpdGxlIGN1c3RvbWl6YXRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCAgICAgICAgICMgRm9udCBmYWNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibmF2eSIsICAgICAgICAjIEZvbnQgY29sb3INCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIsICAgICAgICAgICAgICMgRm9udCBzaXplDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsICAgICAgICAgICAjIEhvcml6b250YWwgYWRqdXN0bWVudA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMSwgICAgICAgICAgICAgIyBWZXJ0aWNhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSAwLCAgICAgICAgICAgICAjIEZvbnQgYW5nbGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0ID0gMSksICAgICAgICMgTGluZSBzcGFjaW5nDQogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuMjUpLCAgIyBDYXB0aW9uIGN1c3RvbWl6YXRpb24NCiAgICAgICAgcGxvdC50YWcgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJpdGFsaWMiKSwgICAjIFRhZyBjdXN0b21pemF0aW9uDQogICAgICAgIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsICAgICAgICAgICAgICAgIyBUaXRsZSBhbmQgc3VidGl0bGUgcG9zaXRpb24gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgKCJwbG90IiBvciAicGFuZWwiKQ0KICAgICAgICBwbG90LmNhcHRpb24ucG9zaXRpb24gPSAicGFuZWwiLCAgICAgICAgICAgICMgQ2FwdGlvbiBwb3NpdGlvbiAoInBsb3QiIG9yICJwYW5lbCIpDQogICAgICAgIHBsb3QudGFnLnBvc2l0aW9uID0gInRvcCIsICAgICAgICAgICAgICAgICAgIyBUYWcgcG9zaXRpb24NCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwxLDQsMSksICJjbSIpKSAgICAgICAjIE1hcmdpbnMgKHQsIHIsIGIsIGwpDQpnaXJhZmUoZ2dvYmogPSBmaWdfZnVsbCkNCmBgYA0KDQoqKnBsb3RfbHkoKSoqDQoNCkluIFIsIGBwbG90X2x5KClgIGlzIGEgZnVuY3Rpb24gZnJvbSB0aGUgKipQbG90bHkgcGFja2FnZSoqIHVzZWQgdG8gY3JlYXRlIGludGVyYWN0aXZlIHBsb3RzICpkaXJlY3RseSBieSBzcGVjaWZ5aW5nIHRoZSBkYXRhIGFuZCBwbG90IHR5cGUqLiBJdCBpcyBhIGNvcmUgZnVuY3Rpb24gaW4gdGhlICoqcGxvdGx5IHBhY2thZ2UqKiBhbmQgc2VydmVzIGFzIHRoZSBzdGFydGluZyBwb2ludCBmb3IgYnVpbGRpbmcgdmFyaW91cyB0eXBlcyBvZiB2aXN1YWxpemF0aW9ucyBzdWNoIGFzIHNjYXR0ZXIgcGxvdHMsIGxpbmUgY2hhcnRzLCBiYXIgY2hhcnRzLCBhbmQgbW9yZS4gIFRoZSBrZXkgZmVhdHVyZXMgYXJlIA0KDQoqICoqRHluYW1pYyBJbnRlcmFjdGl2aXR5Kio6IGl0IHN1cHBvcnRzIHpvb21pbmcsIHBhbm5pbmcsIGFuZCB0b29sdGlwcyAoaG92ZXIgZWZmZWN0cykgZm9yIGV4cGxvcmluZyBkYXRhIGludGVyYWN0aXZlbHkuDQoNCiogKipDdXN0b21pemFibGUgVmlzdWFscyoqOiBJdCBhbGxvd3MgY3VzdG9taXphdGlvbiBvZiBjb2xvcnMsIG1hcmtlcnMsIGF4aXMgbGFiZWxzLCBsZWdlbmRzLCBhbmQgbW9yZS4NCg0KKiAqKldpZGUgUmFuZ2Ugb2YgUGxvdCBUeXBlcyoqOiBJdCBoYW5kbGVzIHNjYXR0ZXIgcGxvdHMsIGJhciBjaGFydHMsIGhpc3RvZ3JhbXMsIDNEIHBsb3RzLCBoZWF0bWFwcywgYW5kIG1vcmUuDQoNCiogKipMYXllcmVkIFBsb3R0aW5nKio6IEl0IGVuYWJsZXMgYWRkaW5nIG11bHRpcGxlIHRyYWNlcyAoZGF0YSBsYXllcnMpIGZvciBjcmVhdGluZyBjb21wbGV4IHZpc3VhbGl6YXRpb25zLg0KDQoqICoqSW50ZWdyYXRpb24qKjogSXQgd29ya3Mgc2VhbWxlc3NseSB3aXRoIFIgTWFya2Rvd24sIFNoaW55LCBhbmQgZGFzaGJvYXJkcy4NCg0KYGBge3J9DQpwbG90bHkuZmlnID0gcGVuZ3VpbnMgJT4lIA0KICAgIHBsb3RfbHkoeCA9IH5iaWxsX2xlbmd0aF9tbSwgDQogICAgICAgICAgICB5ID0gfmZsaXBwZXJfbGVuZ3RoX21tLCANCiAgICAgICAgICAgIGNvbG9yID0gfnNwZWNpZXMsIA0KICAgICAgICAgICAgc3ltYm9sID0gfnNwZWNpZXMsDQogICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIiLCANCiAgICAgICAgICAgIG1vZGUgPSAibWFya2VycyIsICANCiAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDEwKSkgJT4lIA0KICAgIGxheW91dCgNCiAgICAgICAgICAgIHBsb3RfYmdjb2xvciA9ICd3aGl0ZScsDQogICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiQmlsbCBMZW5ndGggKG1tKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHplcm9saW5lID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRpY2tsZW4gPSA1KSwNCiAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRmxpcHBlciBMZW5ndGggKG1tKSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB6ZXJvbGluZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGlja2xlbiA9IDUpLA0KICAgICAgICAgICAgdGl0bGUgPSAiQmlsbCBsZW5ndGggdnMuIGJpbGwgd2lkdGgiLA0KICAgICAgICAgICAgbGVnZW5kID0gbGlzdCh4ID0gMC4wMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAwLjk5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9bGlzdCh0ZXh0PSc8Yj4gU3BlY2llcyBvZiBQZW5ndWlucyA8L2I+JykpLA0KICAgICAgICAgICAgbWFyZ2luID0gbGlzdChsID0gNTAsIHIgPSA1MCwgYiA9IDUwLCB0ID0gNTAsIHBhZCA9IDIwKQ0KICApIA0KcGxvdGx5LmZpZw0KYGBgDQoNCg0KDQoqKmdncGxvdGx5KioNCg0KYGdncGxvdGx5KClgIGlzIGEgcG93ZXJmdWwgdG9vbCBmb3IgYWRkaW5nIGludGVyYWN0aXZpdHkgdG8gZXhpc3RpbmcgKipnZ3Bsb3QyKiogcGxvdHMgaW4gUi4gSXQgYnJpZGdlcyB0aGUgZ2FwIGJldHdlZW4gc3RhdGljIGFuZCBkeW5hbWljIHZpc3VhbGl6YXRpb25zLCBhbGxvd2luZyB1c2VycyB0byByZXRhaW4gdGhlIGZhbWlsaWFyICoqZ2dwbG90MioqIHdvcmtmbG93IHdoaWxlIGJlbmVmaXRpbmcgZnJvbSAqUGxvdGx5J3MqIGludGVyYWN0aXZpdHkuIFRoaXMgbWFrZXMgaXQgYW4gaWRlYWwgY2hvaWNlIGZvciBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLCBwcmVzZW50YXRpb25zLCBhbmQgZHluYW1pYyByZXBvcnRpbmcuDQoNClRoZSBrZXkgZmVhdHVyZXMgb2YgYGdncGxvdGx5KClgIGFyZQ0KDQoqICoqSW50ZXJhY3Rpdml0eSoqOiBJdCBhZGRzIHpvb20sIHBhbiwgaG92ZXIgdG9vbHRpcHMsIGFuZCBsZWdlbmQgaW50ZXJhY3Rpb24gdG8gc3RhdGljICoqZ2dwbG90MioqIHBsb3RzLg0KDQoqICoqUHJlc2VydmVzIGdncGxvdDIgQWVzdGhldGljcyoqOiBJdCBtYWludGFpbnMgdGhlIHZpc3VhbCBkZXNpZ24gYW5kIGN1c3RvbWl6YXRpb24gb2YgKipnZ3Bsb3QyKiogcGxvdHMgd2hpbGUgbWFraW5nIHRoZW0gaW50ZXJhY3RpdmUuDQoNCiogKipDb21wYXRpYmlsaXR5Kio6IEl0IHdvcmtzIHNlYW1sZXNzbHkgd2l0aCBnZ3Bsb3QyLWJhc2VkIHBsb3RzIGFuZCBpbnRlZ3JhdGVzIHdlbGwgd2l0aCBSIE1hcmtkb3duLCBTaGlueSBhcHBzLCBhbmQgZGFzaGJvYXJkcy4NCg0KKiAqKkN1c3RvbWl6YXRpb24qKjogSXQgYWxsb3dzIGZ1cnRoZXIgZW5oYW5jZW1lbnQgb2YgaW50ZXJhY3Rpdml0eSBieSBtb2RpZnlpbmcgdG9vbHRpcHMsIGxlZ2VuZHMsIGFuZCBsYXlvdXRzLg0KDQpgYGB7cn0NCmZpZ19mdWxsIDwtIHBlbmd1aW5zICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSBiaWxsX2RlcHRoX21tLCANCiAgICAgICAgICAgICBjb2xvciA9IHNwZWNpZXMsIHNoYXBlID0gc3BlY2llcykpICsgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSAieSB+IHgiKSArDQogIGxhYnMoeCA9ICJCaWxsIGxlbmd0aCAobW0pIiwgDQogICAgICAgeSA9ICJCaWxsIHdpZHRoIChtbSkiLCANCiAgICAgICB0aXRsZSA9ICJCaWxsIGxlbmd0aCB2cy4gYmlsbCB3aWR0aCIsIA0KICAgICAgIHN1YnRpdGxlID0gIlVzaW5nIGdncGxvdDIgYW5kIGdncGxvdGx5KCkgZnJvbSBwbG90bHkiLA0KICAgICAgIGNvbG9yID0gIlNwZWNpZXMiLCANCiAgICAgICBzaGFwZSA9ICJTcGVjaWVzIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAic2VyaWYiLCAgIyBGb250IGZhbWlseQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsICAgICAgICAgIyBGb250IGZhY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJuYXZ5IiwgICAgICAgICMgRm9udCBjb2xvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxNCwgICAgICAgICAgICAgIyBGb250IHNpemUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgICAgICAgICAgICMgSG9yaXpvbnRhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAxLCAgICAgICAgICAgICAjIFZlcnRpY2FsIGFkanVzdG1lbnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDAsICAgICAgICAgICAgICMgRm9udCBhbmdsZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVoZWlnaHQgPSAxKSwgICAgICAgIyBMaW5lIHNwYWNpbmcNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dCggICAgICAgICAgICAgICAgICAgICMgU3VidGl0bGUgY3VzdG9taXphdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsICAgICAgICAgIyBGb250IGZhY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJuYXZ5IiwgICAgICAgICMgRm9udCBjb2xvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMiwgICAgICAgICAgICAgIyBGb250IHNpemUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgICAgICAgICAgICMgSG9yaXpvbnRhbCBhZGp1c3RtZW50DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAxLCAgICAgICAgICAgICAjIFZlcnRpY2FsIGFkanVzdG1lbnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDApLCAgICAgICAgICAgICMgRm9udCBhbmdsZQ0KICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjI1KSwgICMgQ2FwdGlvbiBjdXN0b21pemF0aW9uDQogICAgICAgIHBsb3QudGFnID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiaXRhbGljIiksICAgIyBUYWcgY3VzdG9taXphdGlvbg0KICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLCAgICAgICAgICAgICAgICMgVGl0bGUgYW5kIHN1YnRpdGxlIHBvc2l0aW9uIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICgicGxvdCIgb3IgInBhbmVsIikNCiAgICAgICAgcGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBhbmVsIiwgICAgICAgICAgICAjIENhcHRpb24gcG9zaXRpb24gKCJwbG90IiBvciAicGFuZWwiKQ0KICAgICAgICBwbG90LnRhZy5wb3NpdGlvbiA9ICJ0b3AiLCAgICAgICAgICAgICAgICAgICMgVGFnIHBvc2l0aW9uDQogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCAiY20iKSkgICAgICAgIyBNYXJnaW5zICh0LCByLCBiLCBsKQ0KDQoNCmdncGxvdGx5KGZpZ19mdWxsKQ0KYGBgDQoNCioqaGlnaGNoYXJ0ZXIoKSoqDQoNCkluIFIsIGhpZ2hjaGFydGVyIGlzIGEgcGFja2FnZSB0aGF0IHByb3ZpZGVzIGFuIGludGVyZmFjZSB0byB0aGUgSGlnaGNoYXJ0cyBKYXZhU2NyaXB0IGxpYnJhcnksIGFsbG93aW5nIHVzZXJzIHRvIGNyZWF0ZSBoaWdobHkgaW50ZXJhY3RpdmUgYW5kIGN1c3RvbWl6YWJsZSB2aXN1YWxpemF0aW9ucy4gSXQgaXMgcGFydGljdWxhcmx5IHdlbGwtc3VpdGVkIGZvciBjcmVhdGluZyBjaGFydHMgd2l0aCBhZHZhbmNlZCBpbnRlcmFjdGl2aXR5LCBwb2xpc2hlZCBhZXN0aGV0aWNzLCBhbmQgYSB3aWRlIHJhbmdlIG9mIGNoYXJ0IHR5cGVzLCBtYWtpbmcgaXQgcG9wdWxhciBmb3IgZGFzaGJvYXJkcywgcHJlc2VudGF0aW9ucywgYW5kIHdlYi1iYXNlZCByZXBvcnRpbmcuDQoNClRoZSBrZXkgZmVhdHVyZXMgb2YgYGhpZ2hjaGFydGVyKClgOg0KDQoqICoqV2lkZSBSYW5nZSBvZiBDaGFydCBUeXBlcyoqOiBJdCBpbmNsdWRlcyBiYXIgY2hhcnRzLCBsaW5lIGNoYXJ0cywgc2NhdHRlciBwbG90cywgaGVhdG1hcHMsIHRyZWVtYXBzLCBhbmQgbW9yZS4gSXQgYWxzbyBzdXBwb3J0cyBhZHZhbmNlZCB2aXN1YWxpemF0aW9ucyBsaWtlIGdhdWdlIGNoYXJ0cywgc3RvY2sgY2hhcnRzLCBhbmQgbWFwcy4NCg0KKiAqKkludGVyYWN0aXZlIEZlYXR1cmVzKio6IEl0IGFsbG93cyBob3ZlciB0b29sdGlwcywgem9vbWluZywgcGFubmluZywgYW5kIGxlZ2VuZHMuIEl0IGFsc28gYWxsb3dzIGR5bmFtaWMgdXBkYXRlcyBhbmQgYW5pbWF0aW9ucy4NCg0KKiAqKkN1c3RvbWl6YWJpbGl0eSoqOiBJdCBvZmZlcnMgZGV0YWlsZWQgY29udHJvbCBvdmVyIGNoYXJ0IGFlc3RoZXRpY3MsIGluY2x1ZGluZyBjb2xvcnMsIHRoZW1lcywgYXhpcyBsYWJlbHMsIGFuZCB0b29sdGlwcy4NCg0KKiAqKkludGVncmF0aW9uKio6IEl0IHdvcmtzIHNlYW1sZXNzbHkgd2l0aCB0aWR5dmVyc2UgZm9yIGRhdGEgbWFuaXB1bGF0aW9uLiBJdCBpcyBjb21wYXRpYmxlIHdpdGggUiBNYXJrZG93biBhbmQgU2hpbnkgZm9yIGNyZWF0aW5nIGR5bmFtaWMgcmVwb3J0cyBhbmQgZGFzaGJvYXJkcy4NCg0KKiAqKlN1cHBvcnQgZm9yIFRoZW1lcyoqOiBJdCBhbGxvd3MgcHJlZGVmaW5lZCB0aGVtZXMgc3VjaCBhcyBgaGNfdGhlbWVfc21wbCgpYCBhbmQgdGhlIGFiaWxpdHkgdG8gY3JlYXRlIGN1c3RvbSB0aGVtZXMuDQoNCg0KDQpgYGB7cn0NCiNwZW5ndWlucyRjb2xvcnMgPSBpZmVsc2UocGVuZ3VpbnMkc3BlY2llcyA9PSJBZGVsaWUiLCIjNDQwMTU0IiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHBlbmd1aW5zJHNwZWNpZXMgPT0iQ2hpbnN0cmFwIiwgIiMyMTkwOEMiLCAiI0ZERTcyNSIgKSkNCiMjDQpmaWdfaGlnaGNoYXJ0IDwtIHBlbmd1aW5zICU+JSANCiAgaGNoYXJ0KHR5cGUgPSAic2NhdHRlciIsIA0KICAgICAgICAgaGNhZXMoeCA9IGJpbGxfbGVuZ3RoX21tLCANCiAgICAgICAgICAgICAgIHkgPSBmbGlwcGVyX2xlbmd0aF9tbSwNCiAgICAgICAgICAgICAgIGdyb3VwID0gc3BlY2llcyksIA0KICAgICAgICAgbWFya2VyID0gbGlzdChyYWRpdXMgPSA1KSwNCiAgICAgICAgIHNob3dJbkxlZ2VuZCA9IEYpICAlPiUgIA0KICBoY194QXhpcyh0aXRsZSA9IGxpc3QodGV4dCA9ICJCaWxsIGxlbmd0aCAobW0pIikpICU+JSAgDQogIGhjX3lBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIkJpbGwgd2lkdGggKG1tKSIpKSAlPiUgIA0KICBoY190aXRsZSh0ZXh0ID0gIkJpbGwgbGVuZ3RoIHZzLiBiaWxsIHdpZHRoIikgJT4lIA0KICAjaGNfY29sb3JzKGNvbG9ycykgJT4lDQogIGhjX2xlZ2VuZChhbGlnbiA9ICJsZWZ0IiwgDQogICAgICAgICAgICB2ZXJ0aWNhbEFsaWduID0gInRvcCIsDQogICAgICAgICAgICBsYXlvdXQgPSAidmVydGljYWwiLCANCiAgICAgICAgICAgIHggPSAwLCANCiAgICAgICAgICAgIHkgPSAxMDApDQojIw0Kc3BlY2llc191bmlxdWUgPC0gc29ydCh1bmlxdWUocGVuZ3VpbnMkc3BlY2llcykpDQpjb2xvcnMgPC0gYygiQWRlbGllIiA9ICIjMmNhZmZlIiwgIkNoaW5zdHJhcCIgPSAiIzU0NGZjNSIsICJHZW50b28iID0gIiMwMGUyNzIiKQ0KIyMjDQpmaWdfaGlnaGNoYXJ0MCA9IGZpZ19oaWdoY2hhcnQNCmZvcihqIGluIDE6Mykgew0KICBwZW5ndWluc19zdWJzZXQgPC0gcGVuZ3VpbnMgJT4lICANCiAgICBmaWx0ZXIoc3BlY2llcyA9PSBzcGVjaWVzX3VuaXF1ZVtqXSkNCiAgIyMNCiAgcmVncmVzc2lvbiA8LSBhdWdtZW50KGxtKGZsaXBwZXJfbGVuZ3RoX21tIH4gYmlsbF9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICAgIGRhdGEgPSBwZW5ndWluc19zdWJzZXQpKQ0KICAjIw0KICBmaWdfaGlnaGNoYXJ0MDEgPC0gZmlnX2hpZ2hjaGFydDAgJT4lICANCiAgICAgIGhjX2FkZF9zZXJpZXMocmVncmVzc2lvbiwgImxpbmUiLCBoY2Flcyh4ID0gYmlsbF9sZW5ndGhfbW0sIHkgPSAuZml0dGVkKSwgDQogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29sb3JzW2pdKQ0KICAjIw0KICBmaWdfaGlnaGNoYXJ0MCA9IGZpZ19oaWdoY2hhcnQwMSAgIyB1cGRhdGluZyBtb2RlbCBvYmplY3QgdG8gYWRkIGFub3RoZXIgbGluZQ0KfQ0KIyMNCmZpZ19oaWdoY2hhcnQwMQ0KYGBgDQoNCiMjIFZpc3VhbGl6aW5nIERpc3RyaWJ1dGlvbnMNCg0KSW4gdGhpcyBzdWJzZWN0aW9uLCB3ZSBkcmF3IGRlbnNpdHkgY3VydmVzIG9mIEJNSSBhY3Jvc3MgdGhlIHNwZWNpZXMgb2YgcGVuZ3VpbnMgd2l0aCB0aGUgYWJvdmUgZm91ciBncmFwaGljYWwgZnVuY3Rpb25zLg0KDQoNCioqZ2dpcmFwaCoqDQoNCmBgYHtyfQ0KZmlnX2RlbnNpdHkgPC0gcGVuZ3VpbnMgJT4lIA0KICAgICAgICAgZ2dwbG90KGFlcyh4ID0gYm9keV9tYXNzX2csIGNvbG9yID0gc3BlY2llcywgZmlsbCA9IHNwZWNpZXMpKSArDQogICAgICAgICAgICAgICAgZ2VvbV9kZW5zaXR5X2ludGVyYWN0aXZlKA0KICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHRvb2x0aXAgPSBwYXN0ZSgiU3BlY2llczoiLCBzcGVjaWVzKSksDQogICAgICAgICAgICAgICAgICAgICAgICBsaW5ld2lkdGggPSAwLjc1LCANCiAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41ICkgICsNCiAgICAgICAgICAgICAgICAgICAgICAgIHhsYWIoIkJNSSBJbmRleCAoZykiKSArICANCiAgICAgICAgICAgZ2d0aXRsZSgiRGVuc2l0eSBDdXJ2ZSBvZiBCTUkgYWNyb3NzIFNwZWNpZXMiKSArDQogICAgICAgICAgIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsMSw0LDEpLCAiY20iKSwNCiAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCmdpcmFmZShnZ29iaiA9IGZpZ19kZW5zaXR5KQ0KYGBgDQoNCg0KKipwbG90X2x5KCkqKiANCg0KYGBge3J9DQpBZGVsaWUuQk1JIDwtIHBlbmd1aW5zJGJvZHlfbWFzc19nW3Blbmd1aW5zJHNwZWNpZXM9PSJBZGVsaWUiXQ0KQ2hpbnN0cmFwLkJNSSAgPC0gcGVuZ3VpbnMkYm9keV9tYXNzX2dbcGVuZ3VpbnMkc3BlY2llcz09IkNoaW5zdHJhcCJdDQpHZW50b28uQk1JICA8LSBwZW5ndWlucyRib2R5X21hc3NfZ1twZW5ndWlucyRzcGVjaWVzPT0iR2VudG9vIl0NCkFkZWxpZS5maXQ9IGRlbnNpdHkoQWRlbGllLkJNSSkNCkNoaW5zdHJhcC5maXQ9IGRlbnNpdHkoQ2hpbnN0cmFwLkJNSSkNCkdlbnRvby5maXQ9IGRlbnNpdHkoR2VudG9vLkJNSSkNCiMgQ3JlYXRlIG92ZXJsYXkgZGVuc2l0eSBjdXJ2ZXMNCnBsb3RfbHkoKSAlPiUNCmFkZF90cmFjZSh4ID0gQWRlbGllLmZpdCR4LCANCiAgICAgICAgICB5ID0gQWRlbGllLmZpdCR5LCANCiAgICAgICAgICBtb2RlID0gImxpbmVzIiwgDQogICAgICAgICAgZmlsbCA9ICJ0b3plcm95IiwgDQogICAgICAgICAgeWF4aXMgPSAieTIiLCANCiAgICAgICAgICBuYW1lID0gIkFkZWxpZSIsDQogICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIpLA0KICAgICAgICAgIG9wYWNpdHkgPSAwLjYpICU+JSANCiAgYWRkX3RyYWNlKHggPSBDaGluc3RyYXAuZml0JHgsIA0KICAgICAgICAgIHkgPSBDaGluc3RyYXAuZml0JHksIA0KICAgICAgICAgIG1vZGUgPSAibGluZXMiLCANCiAgICAgICAgICBmaWxsID0gInRvemVyb3kiLCANCiAgICAgICAgICB5YXhpcyA9ICJ5MiIsIA0KICAgICAgICAgIG5hbWUgPSAiQ2hpbnN0cmFwIiwNCiAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJyZWQiKSwNCiAgICAgICAgICBvcGFjaXR5ID0gMC42KSAlPiUNCiAgICBhZGRfdHJhY2UoeCA9IEdlbnRvby5maXQkeCwgDQogICAgICAgICAgeSA9IEdlbnRvby5maXQkeSwgDQogICAgICAgICAgbW9kZSA9ICJsaW5lcyIsIA0KICAgICAgICAgIGZpbGwgPSAidG96ZXJveSIsIA0KICAgICAgICAgIHlheGlzID0gInkyIiwgDQogICAgICAgICAgbmFtZSA9ICJHZW50b28iLA0KICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInB1cnBsZSIpLA0KICAgICAgICAgIG9wYWNpdHkgPSAwLjYpICU+JQ0KICBsYXlvdXQoDQogICAgICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbnMgb2YgQk1JIGFjcm9zcyBQZW5ndWluIFNwZWNpZXMgIiwNCiAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkJNSSBJbmRleCIpLA0KICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRGVuc2l0eSIpLA0KICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gbGlzdCh0ZXh0ID0gIlNwZWNpZXMiLCBvcmllbnRhdGlvbj0naCcpKSwNCiAgICAgICAgbWFyZ2luID0gbGlzdChsID0gMTAwLCByID0gNTAsIGIgPSA3MCwgdCA9IDEwMCwgcGFkID0gMjApDQogICAgICkNCmBgYA0KDQoNCioqZ2dwbG90bHkoKSoqDQoNCmBgYHtyfQ0KZ2dwbG90bHlfZGVuc2l0eSA8LSBwZW5ndWlucyAlPiUgDQogIGdncGxvdChhZXMoeCA9IGJvZHlfbWFzc19nLCBjb2xvciA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKw0KICBnZW9tX2RlbnNpdHkobGluZXdpZHRoID0gMC43NSwgYWxwaGEgPSAwLjUpICsNCiAgeGxhYigiQk1JIEluZGV4IChnKSIpICsgDQogIGdndGl0bGUoIkRlbnNpdHkgQ3VydmUgb2YgQk1JIGFjcm9zcyBTcGVjaWVzIikgKw0KICAgICAgICAgICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygyLDEsMiwxKSwgImNtIiksDQogICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpnZ3Bsb3RseShnZ3Bsb3RseV9kZW5zaXR5KQ0KYGBgDQoNCioqaGlnaGNoYXJ0ZXIoKSoqDQoNCmBgYHtyfQ0KQWRlbGllIDwtIHBlbmd1aW5zICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiQWRlbGllIikNCkNoaW5zdHJhcCA8LSBwZW5ndWlucyAlPiUgZmlsdGVyKHNwZWNpZXMgPT0gIkNoaW5zdHJhcCIpDQpHZW50b28gPC0gcGVuZ3VpbnMgJT4lIGZpbHRlcihzcGVjaWVzID09ICJHZW50b28iKQ0KaGMgPC0gaGNoYXJ0KA0KICAgICAgICAgICAgIGRlbnNpdHkoQWRlbGllJGJvZHlfbWFzc19nKSwgDQogICAgICAgICAgICAgdHlwZSA9ICJhcmVhIiwgDQogICAgICAgICAgICAgY29sb3IgPSAic3RlZWxibHVlIiwgDQogICAgICAgICAgICAgbmFtZSA9ICJBZGVsaWUiKSAlPiUNCiAgICAgIGhjX2FkZF9zZXJpZXMoDQogICAgICAgICAgICAgZGVuc2l0eShDaGluc3RyYXAkYm9keV9tYXNzX2cpLCANCiAgICAgICAgICAgICB0eXBlID0gImFyZWEiLA0KICAgICAgICAgICAgIGNvbG9yID0gIiNCNzFDMUMiLCANCiAgICAgICAgICAgICBuYW1lID0gIkNoaW5zdHJhcCIpICU+JQ0KICAgICAgaGNfYWRkX3NlcmllcygNCiAgICAgICAgICAgICBkZW5zaXR5KEdlbnRvbyRib2R5X21hc3NfZyksIA0KICAgICAgICAgICAgIHR5cGUgPSAiYXJlYSIsDQogICAgICAgICAgICAgY29sb3IgPSAicHVycGxlIiwgDQogICAgICAgICAgICAgbmFtZSA9ICJHZW50b28iKSAlPiUNCiAgICAgIGhjX3RpdGxlKHRleHQgPSAiRGVuc2l0eSBDdXJ2ZXMgb2YgQk1JIGFjcm9zcyBTcGVjaWVzIikgJT4lIA0KICAgICAgaGNfbGVnZW5kKGFsaWduID0gImxlZnQiLCANCiAgICAgICAgICAgIHZlcnRpY2FsQWxpZ24gPSAidG9wIiwNCiAgICAgICAgICAgIGxheW91dCA9ICJ2ZXJ0aWNhbCIsIA0KICAgICAgICAgICAgeCA9IDAsIA0KICAgICAgICAgICAgeSA9IDEwMCkNCmhjDQpgYGANCg==